2 # run tests on all Samba subprojects and push to a git tree on success
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
6 from __future__ import print_function
7 from subprocess import call, check_call, Popen, PIPE
12 from optparse import OptionParser
15 from email.mime.text import MIMEText
16 from email.mime.base import MIMEBase
17 from email.mime.application import MIMEApplication
18 from email.mime.multipart import MIMEMultipart
19 from distutils.sysconfig import get_python_lib
23 from waflib.Build import CACHE_SUFFIX
25 sys.path.insert(0, "./third_party/waf")
26 from waflib.Build import CACHE_SUFFIX
29 os.environ["PYTHONUNBUFFERED"] = "1"
31 # This speeds up testing remarkably.
32 os.environ['TDB_NO_FSYNC'] = '1'
42 "samba-fileserver": ".",
47 "samba-libs-py2": ".",
49 "samba-none-env": ".",
51 "samba-ad-dc-py2": ".",
52 "samba-ad-dc-ntvfs": ".",
53 "samba-ad-dc-ntvfs-py2": ".",
55 "samba-ad-dc-2-py2": ".",
56 "samba-ad-dc-backup": ".",
57 "samba-systemkrb5": ".",
58 "samba-nopython": ".",
59 "samba-buildpy2-only": ".",
62 "talloc": "lib/talloc",
63 "replace": "lib/replace",
64 "tevent": "lib/tevent",
68 defaulttasks = builddirs.keys()
70 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
71 defaulttasks.remove("samba-o3")
73 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
74 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
76 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
77 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
78 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
79 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
80 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
81 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
82 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs + " ${EXTRA_PYTHON}"
84 if os.environ.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
87 extra_python = "--extra-python=/usr/bin/python2"
90 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
91 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
92 ("make", "make all", "text/plain"),
93 ("install", "make install", "text/plain"),
94 ("test", "make autotest", "text/plain"),
95 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
96 ("clean", "make clean", "text/plain")],
98 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
100 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
101 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
102 ("make", "make -j", "text/plain"),
103 ("test", "make test FAIL_IMMEDIATELY=1 "
105 "--exclude-env=none "
106 "--exclude-env=nt4_dc "
107 "--exclude-env=nt4_member "
108 "--exclude-env=ad_dc "
109 "--exclude-env=ad_dc_ntvfs "
110 "--exclude-env=ad_dc_no_nss "
111 "--exclude-env=fl2003dc "
112 "--exclude-env=fl2008r2dc "
113 "--exclude-env=ad_member "
114 "--exclude-env=ad_member_idmap_rid "
115 "--exclude-env=ad_member_idmap_ad "
116 "--exclude-env=chgdcpass "
117 "--exclude-env=vampire_2000_dc "
118 "--exclude-env=fl2000dc "
119 "--exclude-env=fileserver "
120 "--exclude-env=backupfromdc "
121 "--exclude-env=restoredc "
122 "--exclude-env=renamedc "
123 "--exclude-env=offlinebackupdc "
124 "--exclude-env=labdc "
127 ("install", "make install", "text/plain"),
128 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
129 ("clean", "make clean", "text/plain")],
131 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
132 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
133 ("make", "make -j", "text/plain"),
134 ("test", "make test FAIL_IMMEDIATELY=1 "
136 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
137 ("install", "make install", "text/plain"),
138 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
139 ("clean", "make clean", "text/plain")],
141 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
142 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
143 ("make", "make -j", "text/plain"),
144 ("test", "make test FAIL_IMMEDIATELY=1 "
146 "--include-env=fileserver'", "text/plain"),
147 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
149 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
150 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
151 ("make", "make -j", "text/plain"),
152 ("test", "make test FAIL_IMMEDIATELY=1 "
154 "--include-env=ad_dc "
155 "--include-env=fl2003dc "
156 "--include-env=fl2008r2dc "
157 "--include-env=ad_member "
158 "--include-env=ad_member_idmap_rid "
159 "--include-env=ad_member_idmap_ad'", "text/plain"),
160 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
162 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
163 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
164 ("make", "make -j", "text/plain"),
165 ("test", "make test FAIL_IMMEDIATELY=1 "
167 "--include-env=chgdcpass "
168 "--include-env=vampire_2000_dc "
169 "--include-env=fl2000dc "
170 "--include-env=ad_dc_no_nss "
173 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
175 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
176 # This is currently the longest task, so we don't randomly delay it.
177 "samba-ad-dc-ntvfs": [
178 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
179 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
180 ("make", "make -j", "text/plain"),
181 ("test", "make test FAIL_IMMEDIATELY=1 "
183 "--include-env=ad_dc_ntvfs "
186 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
188 # run the backup/restore testenvs separately as they're fairly standalone
189 # (and CI seems to max out at ~8 different DCs running at once)
190 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
191 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
192 ("make", "make -j", "text/plain"),
193 ("test", "make test FAIL_IMMEDIATELY=1 "
195 "--include-env=backupfromdc "
196 "--include-env=restoredc "
197 "--include-env=renamedc "
198 "--include-env=offlinebackupdc "
199 "--include-env=labdc "
202 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
204 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
205 ("make", "make -j", "text/plain"),
206 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
208 # Test cross-compile infrastructure
209 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
210 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
211 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
212 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
213 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
214 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
215 ("compare-results", "script/compare_cc_results.py "
216 "./bin/c4che/default{} "
217 "./bin-xe/c4che/default{} "
218 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
220 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
221 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
222 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
223 ("make", "make -j", "text/plain"),
224 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
226 "--include-env=ad_dc'", "text/plain"),
227 ("install", "make install", "text/plain"),
228 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
229 ("clean", "make clean", "text/plain")],
231 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
233 # make sure we have tdb around:
234 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
235 ("tdb-make", "cd lib/tdb && make", "text/plain"),
236 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
239 # build samba with cluster support (also building ctdb):
240 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure.developer --picky-developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-cluster-support --bundled-libraries=!tdb", "text/plain"),
241 ("samba-make", "make", "text/plain"),
242 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
243 ("samba-install", "make install", "text/plain"),
244 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
247 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
248 ("clean", "make clean", "text/plain"),
249 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
252 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
253 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
254 ("talloc-make", "cd lib/talloc && make", "text/plain"),
255 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
257 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
258 ("tdb-make", "cd lib/tdb && make", "text/plain"),
259 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
261 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
262 ("tevent-make", "cd lib/tevent && make", "text/plain"),
263 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
265 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
266 ("ldb-make", "cd lib/ldb && make", "text/plain"),
267 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
269 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
270 ("nondevel-make", "make -j", "text/plain"),
271 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
272 ("nondevel-install", "make install", "text/plain"),
273 ("nondevel-dist", "make dist", "text/plain"),
275 # retry with all modules shared
276 ("allshared-distclean", "make distclean", "text/plain"),
277 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
278 ("allshared-make", "make -j", "text/plain")],
281 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
282 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
283 ("make", "make -j", "text/plain"),
284 ("test", "make test "
285 "FAIL_IMMEDIATELY=1 "
287 "--include-env=none'",
291 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
292 # build with all modules static
293 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
294 ("allstatic-make", "make -j", "text/plain"),
295 ("allstatic-test", "make test "
296 "FAIL_IMMEDIATELY=1 "
297 "TESTS='samba3.smb2.create.*nt4_dc'",
300 # retry without any required modules
301 ("none-distclean", "make distclean", "text/plain"),
302 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
303 ("none-make", "make -j", "text/plain"),
305 # retry with nonshared smbd and smbtorture
306 ("nonshared-distclean", "make distclean", "text/plain"),
307 ("nonshared-configure", "./configure.developer " + samba_configure_params + " --bundled-libraries=talloc,tdb,pytdb,ldb,pyldb,tevent,pytevent --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd", "text/plain"),
308 ("nonshared-make", "make -j", "text/plain")],
310 "samba-systemkrb5": [
311 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
312 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
313 ("make", "make -j", "text/plain"),
314 # we currently cannot run a full make test, a limited list of tests could be run
315 # via "make test TESTS=sometests"
316 ("test", "make test FAIL_IMMEDIATELY=1 "
318 "--include-env=ktest'", "text/plain"),
319 ("install", "make install", "text/plain"),
320 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
321 ("clean", "make clean", "text/plain")
324 # Test Samba without python still builds. When this test fails
325 # due to more use of Python, the expectations is that the newly
326 # failing part of the code should be disabled when
327 # --disable-python is set (rather than major work being done to
328 # support this environment). The target here is for vendors
329 # shipping a minimal smbd.
331 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
332 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
333 ("make", "make -j", "text/plain"),
334 ("install", "make install", "text/plain"),
335 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
336 ("clean", "make clean", "text/plain"),
338 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
339 ("talloc-make", "cd lib/talloc && make", "text/plain"),
340 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
342 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
343 ("tdb-make", "cd lib/tdb && make", "text/plain"),
344 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
346 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
347 ("tevent-make", "cd lib/tevent && make", "text/plain"),
348 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
350 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
351 ("ldb-make", "cd lib/ldb && make", "text/plain"),
352 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
354 # retry against installed library packages
355 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
356 ("libs-make", "make -j", "text/plain"),
357 ("libs-install", "make install", "text/plain"),
358 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
359 ("libs-clean", "make clean", "text/plain")
365 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
366 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
367 ("make", "make", "text/plain"),
368 ("install", "make install", "text/plain"),
369 ("test", "make test", "text/plain"),
370 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
371 ("make-no-lmdb", "make", "text/plain"),
372 ("install-no-lmdb", "make install", "text/plain"),
373 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
374 ("distcheck", "make distcheck", "text/plain"),
375 ("clean", "make clean", "text/plain")],
378 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
379 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
380 ("make", "make", "text/plain"),
381 ("install", "make install", "text/plain"),
382 ("test", "make test", "text/plain"),
383 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
384 ("distcheck", "make distcheck", "text/plain"),
385 ("clean", "make clean", "text/plain")],
388 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
389 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
390 ("make", "make", "text/plain"),
391 ("install", "make install", "text/plain"),
392 ("test", "make test", "text/plain"),
393 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
394 ("distcheck", "make distcheck", "text/plain"),
395 ("clean", "make clean", "text/plain")],
398 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
399 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
400 ("make", "make", "text/plain"),
401 ("install", "make install", "text/plain"),
402 ("test", "make test", "text/plain"),
403 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
404 ("distcheck", "make distcheck", "text/plain"),
405 ("clean", "make clean", "text/plain")],
408 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
409 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
410 ("make", "make", "text/plain"),
411 ("install", "make install", "text/plain"),
412 ("test", "make test", "text/plain"),
413 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
414 ("distcheck", "make distcheck", "text/plain"),
415 ("clean", "make clean", "text/plain")],
418 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
419 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
420 ("touch", "touch *.yp", "text/plain"),
421 ("make", "make", "text/plain"),
422 ("test", "make test", "text/plain"),
423 ("install", "make install", "text/plain"),
424 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
425 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
426 ("clean", "make clean", "text/plain")],
428 "samba-buildpy2-only": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
429 ("configure", "PYTHON='python' ./configure.developer --with-selftest-prefix=./bin/ab " + samba_configure_params, "text/plain"),
430 ("make", "PYTHON='python' make -j", "text/plain"),
431 ("install", "PYTHON='python' make install", "text/plain"),
432 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
433 ("clean", "PYTHON='python' make clean", "text/plain")],
436 # these are useful for debugging autobuild
437 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
438 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
450 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
452 show = options.verbose
454 do_print("Running: '%s' in '%s'" % (cmd, dir))
456 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
458 return check_call(cmd, shell=True, cwd=dir)
460 return call(cmd, shell=True, cwd=dir)
463 class builder(object):
464 '''handle build of one directory'''
466 def __init__(self, name, sequence, cp=True, py3=False):
469 if name in builddirs:
470 self.dir = builddirs[name]
474 self.tag = self.name.replace('/', '_')
475 self.sequence = sequence
477 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
478 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
480 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
481 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
482 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
483 self.stdout = open(self.stdout_path, 'w')
484 self.stderr = open(self.stderr_path, 'w')
485 self.stdin = open("/dev/null", 'r')
486 self.sdir = "%s/%s" % (testbase, self.tag)
487 self.prefix = "%s/%s" % (test_prefix, self.tag)
488 run_cmd("rm -rf %s" % self.sdir)
489 run_cmd("rm -rf %s" % self.prefix)
491 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
493 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
496 def start_next(self):
497 if self.next == len(self.sequence):
498 if not options.nocleanup:
499 run_cmd("rm -rf %s" % self.sdir)
500 run_cmd("rm -rf %s" % self.prefix)
501 do_print('%s: Completed OK' % self.name)
504 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
505 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
506 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
508 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
509 # The trailing space is important
510 self.cmd = self.cmd.replace("${PY3_ONLY}", "python2 ")
512 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
513 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
514 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
515 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
516 # if self.output_mime_type == "text/x-subunit":
517 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
519 os.chdir("%s/%s" % (self.sdir, self.dir))
520 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
521 self.proc = Popen(self.cmd, shell=True,
523 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
528 class buildlist(object):
529 '''handle build of multiple directories'''
531 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
534 self.tail_proc = None
537 if options.restrict_tests:
538 tasknames = ["samba-test-only"]
540 tasknames = defaulttasks
542 # If we are only running one test,
543 # do not sleep randomly to wait for it to start
544 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
547 if n not in tasks and n.endswith("-py2"):
553 b = builder(n, tasks[n], cp=n is not "pidl")
556 rebase_remote = "rebaseon"
557 retry_task = [("retry",
559 git remote add -t %s %s %s
563 git describe %s/%s > old_remote_branch.desc
565 git describe %s/%s > remote_branch.desc
566 diff old_remote_branch.desc remote_branch.desc
569 rebase_branch, rebase_remote, rebase_url,
571 rebase_remote, rebase_branch,
573 rebase_remote, rebase_branch
577 self.retry = builder('retry', retry_task, cp=False)
578 self.need_retry = False
581 if self.tail_proc is not None:
582 self.tail_proc.terminate()
583 self.tail_proc.wait()
584 self.tail_proc = None
585 if self.retry is not None:
586 self.retry.proc.terminate()
587 self.retry.proc.wait()
590 if b.proc is not None:
591 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
603 b.status = b.proc.poll()
609 ret = self.retry.proc.poll()
611 self.need_retry = True
621 if options.retry and self.need_retry:
623 do_print("retry needed")
624 return (0, None, None, None, "retry")
627 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
629 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
632 return (0, None, None, None, "All OK")
634 def write_system_info(self):
635 filename = 'system-info.txt'
636 f = open(filename, 'w')
637 for cmd in ['uname -a',
644 'df -m %s' % testbase]:
645 out = run_cmd(cmd, output=True, checkfail=False)
646 print('### %s' % cmd, file=f)
647 print(out.decode('utf8', 'backslashreplace'), file=f)
652 def tarlogs(self, fname):
653 tar = tarfile.open(fname, "w:gz")
655 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
656 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
657 if os.path.exists("autobuild.log"):
658 tar.add("autobuild.log")
659 sys_info = self.write_system_info()
663 def remove_logs(self):
665 os.unlink(b.stdout_path)
666 os.unlink(b.stderr_path)
668 def start_tail(self):
671 cmd.append(b.stdout_path)
672 cmd.append(b.stderr_path)
673 self.tail_proc = Popen(cmd, close_fds=True)
677 if options.nocleanup:
679 run_cmd("stat %s || true" % test_tmpdir, show=True)
680 run_cmd("stat %s" % testbase, show=True)
681 do_print("Cleaning up %r" % cleanup_list)
682 for d in cleanup_list:
683 run_cmd("rm -rf %s" % d)
687 '''get to the top of the git repo'''
690 if os.path.isdir(os.path.join(p, ".git")):
692 p = os.path.abspath(os.path.join(p, '..'))
696 def daemonize(logfile):
698 if pid == 0: # Parent
701 if pid != 0: # Actual daemon
706 import resource # Resource usage information.
707 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
708 if maxfd == resource.RLIM_INFINITY:
709 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
710 for fd in range(0, maxfd):
715 os.open(logfile, os.O_RDWR | os.O_CREAT)
720 def write_pidfile(fname):
721 '''write a pid file, cleanup on exit'''
722 f = open(fname, mode='w')
723 f.write("%u\n" % os.getpid())
727 def rebase_tree(rebase_url, rebase_branch="master"):
728 rebase_remote = "rebaseon"
729 do_print("Rebasing on %s" % rebase_url)
730 run_cmd("git describe HEAD", show=True, dir=test_master)
731 run_cmd("git remote add -t %s %s %s" %
732 (rebase_branch, rebase_remote, rebase_url),
733 show=True, dir=test_master)
734 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
735 if options.fix_whitespace:
736 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
737 (rebase_remote, rebase_branch),
738 show=True, dir=test_master)
740 run_cmd("git rebase --force-rebase %s/%s" %
741 (rebase_remote, rebase_branch),
742 show=True, dir=test_master)
743 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
744 (rebase_remote, rebase_branch),
745 dir=test_master, output=True)
747 do_print("No differences between HEAD and %s/%s - exiting" %
748 (rebase_remote, rebase_branch))
750 run_cmd("git describe %s/%s" %
751 (rebase_remote, rebase_branch),
752 show=True, dir=test_master)
753 run_cmd("git describe HEAD", show=True, dir=test_master)
754 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
755 (rebase_remote, rebase_branch),
756 show=True, dir=test_master)
759 def push_to(push_url, push_branch="master"):
760 push_remote = "pushto"
761 do_print("Pushing to %s" % push_url)
763 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
764 run_cmd("git commit --amend -c HEAD", dir=test_master)
765 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
766 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
767 run_cmd("git remote add -t %s %s %s" %
768 (push_branch, push_remote, push_url),
769 show=True, dir=test_master)
770 run_cmd("git push %s +HEAD:%s" %
771 (push_remote, push_branch),
772 show=True, dir=test_master)
775 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
777 gitroot = find_git_root()
779 raise Exception("Failed to find git root")
781 parser = OptionParser()
782 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
783 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
784 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
785 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
786 default=def_testbase)
787 parser.add_option("", "--passcmd", help="command to run on success", default=None)
788 parser.add_option("", "--verbose", help="show all commands as they are run",
789 default=False, action="store_true")
790 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
791 default=None, type='str')
792 parser.add_option("", "--pushto", help="push to a git url on success",
793 default=None, type='str')
794 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
795 default=False, action="store_true")
796 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
797 default=False, action="store_true")
798 parser.add_option("", "--retry", help="automatically retry if master changes",
799 default=False, action="store_true")
800 parser.add_option("", "--email", help="send email to the given address on failure",
801 type='str', default=None)
802 parser.add_option("", "--email-from", help="send email from the given address",
803 type='str', default="autobuild@samba.org")
804 parser.add_option("", "--email-server", help="send email via the given server",
805 type='str', default='localhost')
806 parser.add_option("", "--always-email", help="always send email, even on success",
808 parser.add_option("", "--daemon", help="daemonize after initial setup",
810 parser.add_option("", "--branch", help="the branch to work on (default=master)",
811 default="master", type='str')
812 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
813 default=gitroot, type='str')
814 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
815 default=False, action="store_true")
816 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
820 def send_email(subject, text, log_tar):
821 if options.email is None:
822 do_print("not sending email because the recipient is not set")
823 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
826 outer = MIMEMultipart()
827 outer['Subject'] = subject
828 outer['To'] = options.email
829 outer['From'] = options.email_from
830 outer['Date'] = email.utils.formatdate(localtime=True)
831 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
832 outer.attach(MIMEText(text, 'plain'))
833 if options.attach_logs:
834 fp = open(log_tar, 'rb')
835 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
837 # Set the filename parameter
838 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
840 content = outer.as_string()
841 s = smtplib.SMTP(options.email_server)
842 s.sendmail(options.email_from, [options.email], content)
847 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
848 elapsed_time, log_base=None, add_log_tail=True):
849 '''send an email to options.email about the failure'''
850 elapsed_minutes = elapsed_time / 60.0
856 Your autobuild on %s failed after %.1f minutes
857 when trying to test %s with the following error:
861 the autobuild has been abandoned. Please fix the error and resubmit.
863 A summary of the autobuild process is here:
866 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
868 if options.restrict_tests:
870 The build was restricted to tests matching %s\n""" % options.restrict_tests
872 if failed_task != 'rebase':
874 You can see logs of the failed task here:
879 or you can get full logs of all tasks in this job here:
883 The top commit for the tree that was built was:
887 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
890 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
891 lines = f.readlines()
892 log_tail = "".join(lines[-50:])
893 num_lines = len(lines)
895 # Also include stderr (compile failures) if < 50 lines of stdout
896 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
897 log_tail += "".join(f.readlines()[-(50 - num_lines):])
900 The last 50 lines of log messages:
906 logs = os.path.join(gitroot, 'logs.tar.gz')
907 send_email('autobuild[%s] failure on %s for task %s during %s'
908 % (options.branch, platform.node(), failed_task, failed_stage),
912 def email_success(elapsed_time, log_base=None):
913 '''send an email to options.email about a successful build'''
919 Your autobuild on %s has succeeded after %.1f minutes.
921 ''' % (platform.node(), elapsed_time / 60.)
923 if options.restrict_tests:
925 The build was restricted to tests matching %s\n""" % options.restrict_tests
930 you can get full logs of all tasks in this job here:
937 The top commit for the tree that was built was:
942 logs = os.path.join(gitroot, 'logs.tar.gz')
943 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
947 (options, args) = parser.parse_args()
950 if options.rebase is None:
951 raise Exception('You can only use --retry if you also rebase')
953 testbase = "%s/b%u" % (options.testbase, os.getpid())
954 test_master = "%s/master" % testbase
955 test_prefix = "%s/prefix" % testbase
956 test_tmpdir = "%s/tmp" % testbase
957 os.environ['TMPDIR'] = test_tmpdir
959 # get the top commit message, for emails
960 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
961 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
964 os.makedirs(testbase)
965 except Exception as reason:
966 raise Exception("Unable to create %s : %s" % (testbase, reason))
967 cleanup_list.append(testbase)
970 logfile = os.path.join(testbase, "log")
971 do_print("Forking into the background, writing progress to %s" % logfile)
974 write_pidfile(gitroot + "/autobuild.pid")
976 start_time = time.time()
980 run_cmd("rm -rf %s" % test_tmpdir, show=True)
981 os.makedirs(test_tmpdir)
982 # The waf uninstall code removes empty directories all the way
983 # up the tree. Creating a file in test_tmpdir stops it from
985 run_cmd("touch %s" % os.path.join(test_tmpdir,
986 ".directory-is-not-empty"), show=True)
987 run_cmd("stat %s" % test_tmpdir, show=True)
988 run_cmd("stat %s" % testbase, show=True)
989 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
996 if options.rebase is not None:
997 rebase_tree(options.rebase, rebase_branch=options.branch)
999 cleanup_list.append(gitroot + "/autobuild.pid")
1001 elapsed_time = time.time() - start_time
1002 email_failure(-1, 'rebase', 'rebase', 'rebase',
1003 'rebase on %s failed' % options.branch,
1004 elapsed_time, log_base=options.log_base)
1006 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
1009 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
1010 if status != 0 or errstr != "retry":
1017 cleanup_list.append(gitroot + "/autobuild.pid")
1023 do_print("waiting for tail to flush")
1026 elapsed_time = time.time() - start_time
1028 if options.passcmd is not None:
1029 do_print("Running passcmd: %s" % options.passcmd)
1030 run_cmd(options.passcmd, dir=test_master)
1031 if options.pushto is not None:
1032 push_to(options.pushto, push_branch=options.branch)
1033 if options.keeplogs or options.attach_logs:
1034 blist.tarlogs("logs.tar.gz")
1035 do_print("Logs in logs.tar.gz")
1036 if options.always_email:
1037 email_success(elapsed_time, log_base=options.log_base)
1043 # something failed, gather a tar of the logs
1044 blist.tarlogs("logs.tar.gz")
1046 if options.email is not None:
1047 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1048 elapsed_time, log_base=options.log_base)
1050 elapsed_minutes = elapsed_time / 60.0
1053 ####################################################################
1057 Your autobuild[%s] on %s failed after %.1f minutes
1058 when trying to test %s with the following error:
1062 the autobuild has been abandoned. Please fix the error and resubmit.
1064 ####################################################################
1066 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1070 do_print("Logs in logs.tar.gz")