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'
40 "samba-fileserver": ".",
46 "samba-none-env": ".",
48 "samba-ad-dc-ntvfs": ".",
50 "samba-ad-dc-backup": ".",
51 "samba-systemkrb5": ".",
52 "samba-nopython": ".",
53 "samba-buildpy2-only": ".",
56 "talloc": "lib/talloc",
57 "replace": "lib/replace",
58 "tevent": "lib/tevent",
62 defaulttasks = builddirs.keys()
64 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
65 defaulttasks.remove("samba-o3")
67 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
68 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
70 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
71 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
72 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
73 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
74 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
75 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
76 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs + " ${EXTRA_PYTHON}"
78 if os.environ.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
81 extra_python = "--extra-python=/usr/bin/python2"
84 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
85 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
86 ("make", "make all", "text/plain"),
87 ("install", "make install", "text/plain"),
88 ("test", "make autotest", "text/plain"),
89 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
90 ("clean", "make clean", "text/plain")],
92 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
94 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
95 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
96 ("make", "make -j", "text/plain"),
97 ("test", "make test FAIL_IMMEDIATELY=1 "
100 "--exclude-env=nt4_dc "
101 "--exclude-env=nt4_member "
102 "--exclude-env=ad_dc "
103 "--exclude-env=ad_dc_ntvfs "
104 "--exclude-env=ad_dc_no_nss "
105 "--exclude-env=fl2003dc "
106 "--exclude-env=fl2008r2dc "
107 "--exclude-env=ad_member "
108 "--exclude-env=ad_member_idmap_rid "
109 "--exclude-env=ad_member_idmap_ad "
110 "--exclude-env=chgdcpass "
111 "--exclude-env=vampire_2000_dc "
112 "--exclude-env=fl2000dc "
113 "--exclude-env=fileserver "
114 "--exclude-env=backupfromdc "
115 "--exclude-env=restoredc "
116 "--exclude-env=renamedc "
117 "--exclude-env=offlinebackupdc "
118 "--exclude-env=labdc "
121 ("install", "make install", "text/plain"),
122 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
123 ("clean", "make clean", "text/plain")],
125 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
126 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
127 ("make", "make -j", "text/plain"),
128 ("test", "make test FAIL_IMMEDIATELY=1 "
130 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
131 ("install", "make install", "text/plain"),
132 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
133 ("clean", "make clean", "text/plain")],
135 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
136 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
137 ("make", "make -j", "text/plain"),
138 ("test", "make test FAIL_IMMEDIATELY=1 "
140 "--include-env=fileserver'", "text/plain"),
141 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
143 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
144 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
145 ("make", "make -j", "text/plain"),
146 ("test", "make test FAIL_IMMEDIATELY=1 "
148 "--include-env=ad_dc "
149 "--include-env=fl2003dc "
150 "--include-env=fl2008r2dc "
151 "--include-env=ad_member "
152 "--include-env=ad_member_idmap_rid "
153 "--include-env=ad_member_idmap_ad'", "text/plain"),
154 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
156 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
157 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
158 ("make", "make -j", "text/plain"),
159 ("test", "make test FAIL_IMMEDIATELY=1 "
161 "--include-env=chgdcpass "
162 "--include-env=vampire_2000_dc "
163 "--include-env=fl2000dc "
164 "--include-env=ad_dc_no_nss "
167 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
169 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
170 # This is currently the longest task, so we don't randomly delay it.
171 "samba-ad-dc-ntvfs": [
172 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
173 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
174 ("make", "make -j", "text/plain"),
175 ("test", "make test FAIL_IMMEDIATELY=1 "
177 "--include-env=ad_dc_ntvfs "
180 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
182 # run the backup/restore testenvs separately as they're fairly standalone
183 # (and CI seems to max out at ~8 different DCs running at once)
184 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
185 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
186 ("make", "make -j", "text/plain"),
187 ("test", "make test FAIL_IMMEDIATELY=1 "
189 "--include-env=backupfromdc "
190 "--include-env=restoredc "
191 "--include-env=renamedc "
192 "--include-env=offlinebackupdc "
193 "--include-env=labdc "
196 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
198 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
199 ("make", "make -j", "text/plain"),
200 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
202 # Test cross-compile infrastructure
203 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
204 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
205 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
206 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
207 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
208 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
209 ("compare-results", "script/compare_cc_results.py "
210 "./bin/c4che/default{} "
211 "./bin-xe/c4che/default{} "
212 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
214 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
215 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
216 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
217 ("make", "make -j", "text/plain"),
218 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
220 "--include-env=ad_dc'", "text/plain"),
221 ("install", "make install", "text/plain"),
222 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
223 ("clean", "make clean", "text/plain")],
225 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
227 # make sure we have tdb around:
228 ("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"),
229 ("tdb-make", "cd lib/tdb && make", "text/plain"),
230 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
233 # build samba with cluster support (also building ctdb):
234 ("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"),
235 ("samba-make", "make", "text/plain"),
236 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
237 ("samba-install", "make install", "text/plain"),
238 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
241 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
242 ("clean", "make clean", "text/plain"),
243 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
246 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
247 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
248 ("talloc-make", "cd lib/talloc && make", "text/plain"),
249 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
251 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
252 ("tdb-make", "cd lib/tdb && make", "text/plain"),
253 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
255 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
256 ("tevent-make", "cd lib/tevent && make", "text/plain"),
257 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
259 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
260 ("ldb-make", "cd lib/ldb && make", "text/plain"),
261 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
263 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
264 ("nondevel-make", "make -j", "text/plain"),
265 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
266 ("nondevel-install", "make install", "text/plain"),
267 ("nondevel-dist", "make dist", "text/plain"),
269 # retry with all modules shared
270 ("allshared-distclean", "make distclean", "text/plain"),
271 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
272 ("allshared-make", "make -j", "text/plain")],
275 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
276 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
277 ("make", "make -j", "text/plain"),
278 ("test", "make test "
279 "FAIL_IMMEDIATELY=1 "
281 "--include-env=none'",
285 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
286 # build with all modules static
287 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
288 ("allstatic-make", "make -j", "text/plain"),
289 ("allstatic-test", "make test "
290 "FAIL_IMMEDIATELY=1 "
291 "TESTS='samba3.smb2.create.*nt4_dc'",
294 # retry without any required modules
295 ("none-distclean", "make distclean", "text/plain"),
296 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
297 ("none-make", "make -j", "text/plain"),
299 # retry with nonshared smbd and smbtorture
300 ("nonshared-distclean", "make distclean", "text/plain"),
301 ("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"),
302 ("nonshared-make", "make -j", "text/plain")],
304 "samba-systemkrb5": [
305 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
306 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
307 ("make", "make -j", "text/plain"),
308 # we currently cannot run a full make test, a limited list of tests could be run
309 # via "make test TESTS=sometests"
310 ("test", "make test FAIL_IMMEDIATELY=1 "
312 "--include-env=ktest'", "text/plain"),
313 ("install", "make install", "text/plain"),
314 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
315 ("clean", "make clean", "text/plain")
318 # Test Samba without python still builds. When this test fails
319 # due to more use of Python, the expectations is that the newly
320 # failing part of the code should be disabled when
321 # --disable-python is set (rather than major work being done to
322 # support this environment). The target here is for vendors
323 # shipping a minimal smbd.
325 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
326 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
327 ("make", "make -j", "text/plain"),
328 ("install", "make install", "text/plain"),
329 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
330 ("clean", "make clean", "text/plain"),
332 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
333 ("talloc-make", "cd lib/talloc && make", "text/plain"),
334 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
336 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
337 ("tdb-make", "cd lib/tdb && make", "text/plain"),
338 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
340 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
341 ("tevent-make", "cd lib/tevent && make", "text/plain"),
342 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
344 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
345 ("ldb-make", "cd lib/ldb && make", "text/plain"),
346 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
348 # retry against installed library packages
349 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
350 ("libs-make", "make -j", "text/plain"),
351 ("libs-install", "make install", "text/plain"),
352 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
353 ("libs-clean", "make clean", "text/plain")
359 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
360 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
361 ("make", "make", "text/plain"),
362 ("install", "make install", "text/plain"),
363 ("test", "make test", "text/plain"),
364 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
365 ("make-no-lmdb", "make", "text/plain"),
366 ("install-no-lmdb", "make install", "text/plain"),
367 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
368 ("distcheck", "make distcheck", "text/plain"),
369 ("clean", "make clean", "text/plain")],
372 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
373 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
374 ("make", "make", "text/plain"),
375 ("install", "make install", "text/plain"),
376 ("test", "make test", "text/plain"),
377 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
378 ("distcheck", "make distcheck", "text/plain"),
379 ("clean", "make clean", "text/plain")],
382 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
383 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
384 ("make", "make", "text/plain"),
385 ("install", "make install", "text/plain"),
386 ("test", "make test", "text/plain"),
387 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
388 ("distcheck", "make distcheck", "text/plain"),
389 ("clean", "make clean", "text/plain")],
392 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
393 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
394 ("make", "make", "text/plain"),
395 ("install", "make install", "text/plain"),
396 ("test", "make test", "text/plain"),
397 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
398 ("distcheck", "make distcheck", "text/plain"),
399 ("clean", "make clean", "text/plain")],
402 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
403 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
404 ("make", "make", "text/plain"),
405 ("install", "make install", "text/plain"),
406 ("test", "make test", "text/plain"),
407 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
408 ("distcheck", "make distcheck", "text/plain"),
409 ("clean", "make clean", "text/plain")],
412 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
413 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
414 ("touch", "touch *.yp", "text/plain"),
415 ("make", "make", "text/plain"),
416 ("test", "make test", "text/plain"),
417 ("install", "make install", "text/plain"),
418 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
419 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
420 ("clean", "make clean", "text/plain")],
422 "samba-buildpy2-only": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
423 ("configure", "PYTHON='python' ./configure.developer --with-selftest-prefix=./bin/ab " + samba_configure_params, "text/plain"),
424 ("make", "PYTHON='python' make -j", "text/plain"),
425 ("install", "PYTHON='python' make install", "text/plain"),
426 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
427 ("clean", "PYTHON='python' make clean", "text/plain")],
430 # these are useful for debugging autobuild
431 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
432 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
444 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
446 show = options.verbose
448 do_print("Running: '%s' in '%s'" % (cmd, dir))
450 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
452 return check_call(cmd, shell=True, cwd=dir)
454 return call(cmd, shell=True, cwd=dir)
457 class builder(object):
458 '''handle build of one directory'''
460 def __init__(self, name, sequence, cp=True, py2=False):
463 if name in builddirs:
464 self.dir = builddirs[name]
468 self.tag = self.name.replace('/', '_')
469 self.sequence = sequence
471 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
472 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
474 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
475 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
476 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
477 self.stdout = open(self.stdout_path, 'w')
478 self.stderr = open(self.stderr_path, 'w')
479 self.stdin = open("/dev/null", 'r')
480 self.sdir = "%s/%s" % (testbase, self.tag)
481 self.prefix = "%s/%s" % (test_prefix, self.tag)
482 run_cmd("rm -rf %s" % self.sdir)
483 run_cmd("rm -rf %s" % self.prefix)
485 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
487 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
490 def start_next(self):
491 if self.next == len(self.sequence):
492 if not options.nocleanup:
493 run_cmd("rm -rf %s" % self.sdir)
494 run_cmd("rm -rf %s" % self.prefix)
495 do_print('%s: Completed OK' % self.name)
498 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
499 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
500 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
502 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
504 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
505 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
506 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
507 # if self.output_mime_type == "text/x-subunit":
508 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
510 os.chdir("%s/%s" % (self.sdir, self.dir))
511 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
512 self.proc = Popen(self.cmd, shell=True,
514 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
519 class buildlist(object):
520 '''handle build of multiple directories'''
522 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
525 self.tail_proc = None
528 if options.restrict_tests:
529 tasknames = ["samba-test-only"]
531 tasknames = defaulttasks
533 # If we are only running one test,
534 # do not sleep randomly to wait for it to start
535 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
538 if n not in tasks and n.endswith("-py2"):
544 b = builder(n, tasks[n], cp=n is not "pidl")
547 rebase_remote = "rebaseon"
548 retry_task = [("retry",
550 git remote add -t %s %s %s
554 git describe %s/%s > old_remote_branch.desc
556 git describe %s/%s > remote_branch.desc
557 diff old_remote_branch.desc remote_branch.desc
560 rebase_branch, rebase_remote, rebase_url,
562 rebase_remote, rebase_branch,
564 rebase_remote, rebase_branch
568 self.retry = builder('retry', retry_task, cp=False)
569 self.need_retry = False
572 if self.tail_proc is not None:
573 self.tail_proc.terminate()
574 self.tail_proc.wait()
575 self.tail_proc = None
576 if self.retry is not None:
577 self.retry.proc.terminate()
578 self.retry.proc.wait()
581 if b.proc is not None:
582 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
594 b.status = b.proc.poll()
600 ret = self.retry.proc.poll()
602 self.need_retry = True
612 if options.retry and self.need_retry:
614 do_print("retry needed")
615 return (0, None, None, None, "retry")
618 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
620 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
623 return (0, None, None, None, "All OK")
625 def write_system_info(self):
626 filename = 'system-info.txt'
627 f = open(filename, 'w')
628 for cmd in ['uname -a',
635 'df -m %s' % testbase]:
636 out = run_cmd(cmd, output=True, checkfail=False)
637 print('### %s' % cmd, file=f)
638 print(out.decode('utf8', 'backslashreplace'), file=f)
643 def tarlogs(self, fname):
644 tar = tarfile.open(fname, "w:gz")
646 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
647 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
648 if os.path.exists("autobuild.log"):
649 tar.add("autobuild.log")
650 sys_info = self.write_system_info()
654 def remove_logs(self):
656 os.unlink(b.stdout_path)
657 os.unlink(b.stderr_path)
659 def start_tail(self):
662 cmd.append(b.stdout_path)
663 cmd.append(b.stderr_path)
664 self.tail_proc = Popen(cmd, close_fds=True)
668 if options.nocleanup:
670 run_cmd("stat %s || true" % test_tmpdir, show=True)
671 run_cmd("stat %s" % testbase, show=True)
672 do_print("Cleaning up %r" % cleanup_list)
673 for d in cleanup_list:
674 run_cmd("rm -rf %s" % d)
678 '''get to the top of the git repo'''
681 if os.path.isdir(os.path.join(p, ".git")):
683 p = os.path.abspath(os.path.join(p, '..'))
687 def daemonize(logfile):
689 if pid == 0: # Parent
692 if pid != 0: # Actual daemon
697 import resource # Resource usage information.
698 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
699 if maxfd == resource.RLIM_INFINITY:
700 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
701 for fd in range(0, maxfd):
706 os.open(logfile, os.O_RDWR | os.O_CREAT)
711 def write_pidfile(fname):
712 '''write a pid file, cleanup on exit'''
713 f = open(fname, mode='w')
714 f.write("%u\n" % os.getpid())
718 def rebase_tree(rebase_url, rebase_branch="master"):
719 rebase_remote = "rebaseon"
720 do_print("Rebasing on %s" % rebase_url)
721 run_cmd("git describe HEAD", show=True, dir=test_master)
722 run_cmd("git remote add -t %s %s %s" %
723 (rebase_branch, rebase_remote, rebase_url),
724 show=True, dir=test_master)
725 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
726 if options.fix_whitespace:
727 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
728 (rebase_remote, rebase_branch),
729 show=True, dir=test_master)
731 run_cmd("git rebase --force-rebase %s/%s" %
732 (rebase_remote, rebase_branch),
733 show=True, dir=test_master)
734 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
735 (rebase_remote, rebase_branch),
736 dir=test_master, output=True)
738 do_print("No differences between HEAD and %s/%s - exiting" %
739 (rebase_remote, rebase_branch))
741 run_cmd("git describe %s/%s" %
742 (rebase_remote, rebase_branch),
743 show=True, dir=test_master)
744 run_cmd("git describe HEAD", show=True, dir=test_master)
745 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
746 (rebase_remote, rebase_branch),
747 show=True, dir=test_master)
750 def push_to(push_url, push_branch="master"):
751 push_remote = "pushto"
752 do_print("Pushing to %s" % push_url)
754 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
755 run_cmd("git commit --amend -c HEAD", dir=test_master)
756 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
757 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
758 run_cmd("git remote add -t %s %s %s" %
759 (push_branch, push_remote, push_url),
760 show=True, dir=test_master)
761 run_cmd("git push %s +HEAD:%s" %
762 (push_remote, push_branch),
763 show=True, dir=test_master)
766 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
768 gitroot = find_git_root()
770 raise Exception("Failed to find git root")
772 parser = OptionParser()
773 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
774 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
775 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
776 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
777 default=def_testbase)
778 parser.add_option("", "--passcmd", help="command to run on success", default=None)
779 parser.add_option("", "--verbose", help="show all commands as they are run",
780 default=False, action="store_true")
781 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
782 default=None, type='str')
783 parser.add_option("", "--pushto", help="push to a git url on success",
784 default=None, type='str')
785 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
786 default=False, action="store_true")
787 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
788 default=False, action="store_true")
789 parser.add_option("", "--retry", help="automatically retry if master changes",
790 default=False, action="store_true")
791 parser.add_option("", "--email", help="send email to the given address on failure",
792 type='str', default=None)
793 parser.add_option("", "--email-from", help="send email from the given address",
794 type='str', default="autobuild@samba.org")
795 parser.add_option("", "--email-server", help="send email via the given server",
796 type='str', default='localhost')
797 parser.add_option("", "--always-email", help="always send email, even on success",
799 parser.add_option("", "--daemon", help="daemonize after initial setup",
801 parser.add_option("", "--branch", help="the branch to work on (default=master)",
802 default="master", type='str')
803 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
804 default=gitroot, type='str')
805 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
806 default=False, action="store_true")
807 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
811 def send_email(subject, text, log_tar):
812 if options.email is None:
813 do_print("not sending email because the recipient is not set")
814 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
817 outer = MIMEMultipart()
818 outer['Subject'] = subject
819 outer['To'] = options.email
820 outer['From'] = options.email_from
821 outer['Date'] = email.utils.formatdate(localtime=True)
822 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
823 outer.attach(MIMEText(text, 'plain'))
824 if options.attach_logs:
825 fp = open(log_tar, 'rb')
826 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
828 # Set the filename parameter
829 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
831 content = outer.as_string()
832 s = smtplib.SMTP(options.email_server)
833 s.sendmail(options.email_from, [options.email], content)
838 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
839 elapsed_time, log_base=None, add_log_tail=True):
840 '''send an email to options.email about the failure'''
841 elapsed_minutes = elapsed_time / 60.0
847 Your autobuild on %s failed after %.1f minutes
848 when trying to test %s with the following error:
852 the autobuild has been abandoned. Please fix the error and resubmit.
854 A summary of the autobuild process is here:
857 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
859 if options.restrict_tests:
861 The build was restricted to tests matching %s\n""" % options.restrict_tests
863 if failed_task != 'rebase':
865 You can see logs of the failed task here:
870 or you can get full logs of all tasks in this job here:
874 The top commit for the tree that was built was:
878 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
881 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
882 lines = f.readlines()
883 log_tail = "".join(lines[-50:])
884 num_lines = len(lines)
886 # Also include stderr (compile failures) if < 50 lines of stdout
887 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
888 log_tail += "".join(f.readlines()[-(50 - num_lines):])
891 The last 50 lines of log messages:
897 logs = os.path.join(gitroot, 'logs.tar.gz')
898 send_email('autobuild[%s] failure on %s for task %s during %s'
899 % (options.branch, platform.node(), failed_task, failed_stage),
903 def email_success(elapsed_time, log_base=None):
904 '''send an email to options.email about a successful build'''
910 Your autobuild on %s has succeeded after %.1f minutes.
912 ''' % (platform.node(), elapsed_time / 60.)
914 if options.restrict_tests:
916 The build was restricted to tests matching %s\n""" % options.restrict_tests
921 you can get full logs of all tasks in this job here:
928 The top commit for the tree that was built was:
933 logs = os.path.join(gitroot, 'logs.tar.gz')
934 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
938 (options, args) = parser.parse_args()
941 if options.rebase is None:
942 raise Exception('You can only use --retry if you also rebase')
944 testbase = "%s/b%u" % (options.testbase, os.getpid())
945 test_master = "%s/master" % testbase
946 test_prefix = "%s/prefix" % testbase
947 test_tmpdir = "%s/tmp" % testbase
948 os.environ['TMPDIR'] = test_tmpdir
950 # get the top commit message, for emails
951 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
952 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
955 os.makedirs(testbase)
956 except Exception as reason:
957 raise Exception("Unable to create %s : %s" % (testbase, reason))
958 cleanup_list.append(testbase)
961 logfile = os.path.join(testbase, "log")
962 do_print("Forking into the background, writing progress to %s" % logfile)
965 write_pidfile(gitroot + "/autobuild.pid")
967 start_time = time.time()
971 run_cmd("rm -rf %s" % test_tmpdir, show=True)
972 os.makedirs(test_tmpdir)
973 # The waf uninstall code removes empty directories all the way
974 # up the tree. Creating a file in test_tmpdir stops it from
976 run_cmd("touch %s" % os.path.join(test_tmpdir,
977 ".directory-is-not-empty"), show=True)
978 run_cmd("stat %s" % test_tmpdir, show=True)
979 run_cmd("stat %s" % testbase, show=True)
980 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
987 if options.rebase is not None:
988 rebase_tree(options.rebase, rebase_branch=options.branch)
990 cleanup_list.append(gitroot + "/autobuild.pid")
992 elapsed_time = time.time() - start_time
993 email_failure(-1, 'rebase', 'rebase', 'rebase',
994 'rebase on %s failed' % options.branch,
995 elapsed_time, log_base=options.log_base)
997 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
1000 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
1001 if status != 0 or errstr != "retry":
1008 cleanup_list.append(gitroot + "/autobuild.pid")
1014 do_print("waiting for tail to flush")
1017 elapsed_time = time.time() - start_time
1019 if options.passcmd is not None:
1020 do_print("Running passcmd: %s" % options.passcmd)
1021 run_cmd(options.passcmd, dir=test_master)
1022 if options.pushto is not None:
1023 push_to(options.pushto, push_branch=options.branch)
1024 if options.keeplogs or options.attach_logs:
1025 blist.tarlogs("logs.tar.gz")
1026 do_print("Logs in logs.tar.gz")
1027 if options.always_email:
1028 email_success(elapsed_time, log_base=options.log_base)
1034 # something failed, gather a tar of the logs
1035 blist.tarlogs("logs.tar.gz")
1037 if options.email is not None:
1038 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1039 elapsed_time, log_base=options.log_base)
1041 elapsed_minutes = elapsed_time / 60.0
1044 ####################################################################
1048 Your autobuild[%s] on %s failed after %.1f minutes
1049 when trying to test %s with the following error:
1053 the autobuild has been abandoned. Please fix the error and resubmit.
1055 ####################################################################
1057 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1061 do_print("Logs in logs.tar.gz")