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-py3": ".",
49 "samba-none-env": ".",
50 "samba-none-env-py3": ".",
52 "samba-ad-dc-py3": ".",
54 "samba-ad-dc-2-py3": ".",
55 "samba-systemkrb5": ".",
56 "samba-nopython": ".",
57 "samba-buildpy3-only": ".",
60 "talloc": "lib/talloc",
61 "replace": "lib/replace",
62 "tevent": "lib/tevent",
66 defaulttasks = builddirs.keys()
68 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
69 defaulttasks.remove("samba-o3")
71 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
72 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
74 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
75 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
76 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
77 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
78 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
79 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
80 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs + " ${EXTRA_PYTHON}"
82 if os.environ.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
85 extra_python = "--extra-python=/usr/bin/python3"
88 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
89 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
90 ("make", "make all", "text/plain"),
91 ("install", "make install", "text/plain"),
92 ("test", "make autotest", "text/plain"),
93 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
94 ("clean", "make clean", "text/plain")],
96 # We have 'test' before 'install' because, 'test' should work without 'install (runs ad_dc_ntvfs and all the other envs)'
97 "samba": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
98 ("make", "make -j", "text/plain"),
99 ("test", "make test FAIL_IMMEDIATELY=1 "
101 "--exclude-env=none "
102 "--exclude-env=nt4_dc "
103 "--exclude-env=nt4_member "
104 "--exclude-env=ad_dc "
105 "--exclude-env=ad_dc_no_nss "
106 "--exclude-env=fl2003dc "
107 "--exclude-env=fl2008r2dc "
108 "--exclude-env=ad_member "
109 "--exclude-env=ad_member_idmap_rid "
110 "--exclude-env=ad_member_idmap_ad "
111 "--exclude-env=chgdcpass "
112 "--exclude-env=vampire_2000_dc "
113 "--exclude-env=fl2000dc "
114 "--exclude-env=fileserver "
115 "--exclude-env=backupfromdc "
116 "--exclude-env=restoredc "
117 "--exclude-env=renamedc "
118 "--exclude-env=offlinebackupdc "
119 "--exclude-env=labdc "
122 ("install", "make install", "text/plain"),
123 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
124 ("clean", "make clean", "text/plain")],
126 # We split out this so the isolated nt4_dc tests do not wait for ad_dc or ad_dc_ntvfs tests (which are long)
127 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
128 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
129 ("make", "make -j", "text/plain"),
130 ("test", "make test FAIL_IMMEDIATELY=1 "
132 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
133 ("install", "make install", "text/plain"),
134 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
135 ("clean", "make clean", "text/plain")],
137 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
138 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
139 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
140 ("make", "make -j", "text/plain"),
141 ("test", "make test FAIL_IMMEDIATELY=1 "
143 "--include-env=fileserver'", "text/plain"),
144 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
146 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
147 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
148 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
149 ("make", "make -j", "text/plain"),
150 ("test", "make test FAIL_IMMEDIATELY=1 "
152 "--include-env=ad_dc "
153 "--include-env=fl2003dc "
154 "--include-env=fl2008r2dc "
155 "--include-env=ad_member "
156 "--include-env=ad_member_idmap_rid "
157 "--include-env=ad_member_idmap_ad'", "text/plain"),
158 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
160 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
161 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
162 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
163 ("make", "make -j", "text/plain"),
164 ("test", "make test FAIL_IMMEDIATELY=1 "
166 "--include-env=chgdcpass "
167 "--include-env=vampire_2000_dc "
168 "--include-env=fl2000dc "
169 "--include-env=ad_dc_no_nss "
170 "--include-env=backupfromdc "
171 "--include-env=restoredc "
172 "--include-env=renamedc "
173 "--include-env=offlinebackupdc "
174 "--include-env=labdc "
177 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
179 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
180 ("make", "make -j", "text/plain"),
181 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
183 # Test cross-compile infrastructure
184 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
185 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
186 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
187 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
188 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
189 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
190 ("compare-results", "script/compare_cc_results.py "
191 "./bin/c4che/default{} "
192 "./bin-xe/c4che/default{} "
193 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
195 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
196 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
197 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
198 ("make", "make -j", "text/plain"),
199 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
201 "--include-env=ad_dc'", "text/plain"),
202 ("install", "make install", "text/plain"),
203 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
204 ("clean", "make clean", "text/plain")],
206 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
208 # make sure we have tdb around:
209 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
210 ("tdb-make", "cd lib/tdb && make", "text/plain"),
211 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
214 # build samba with cluster support (also building ctdb):
215 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$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"),
216 ("samba-make", "make", "text/plain"),
217 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
218 ("samba-install", "make install", "text/plain"),
219 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
222 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
223 ("clean", "make clean", "text/plain"),
224 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
227 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
228 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
229 ("talloc-make", "cd lib/talloc && make", "text/plain"),
230 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
232 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
233 ("tdb-make", "cd lib/tdb && make", "text/plain"),
234 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
236 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
237 ("tevent-make", "cd lib/tevent && make", "text/plain"),
238 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
240 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
241 ("ldb-make", "cd lib/ldb && make", "text/plain"),
242 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
244 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
245 ("nondevel-make", "make -j", "text/plain"),
246 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
247 ("nondevel-install", "make install", "text/plain"),
248 ("nondevel-dist", "make dist", "text/plain"),
250 # retry with all modules shared
251 ("allshared-distclean", "make distclean", "text/plain"),
252 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
253 ("allshared-make", "make -j", "text/plain")],
256 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
257 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
258 ("make", "make -j", "text/plain"),
259 ("test", "make test "
260 "FAIL_IMMEDIATELY=1 "
262 "--include-env=none'",
266 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
267 # build with all modules static
268 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
269 ("allstatic-make", "make -j", "text/plain"),
270 ("allstatic-test", "make test "
271 "FAIL_IMMEDIATELY=1 "
272 "TESTS='samba3.smb2.create.*nt4_dc'",
275 # retry without any required modules
276 ("none-distclean", "make distclean", "text/plain"),
277 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
278 ("none-make", "make -j", "text/plain"),
280 # retry with nonshared smbd and smbtorture
281 ("nonshared-distclean", "make distclean", "text/plain"),
282 ("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"),
283 ("nonshared-make", "make -j", "text/plain")],
285 "samba-systemkrb5": [
286 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
287 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
288 ("make", "make -j", "text/plain"),
289 # we currently cannot run a full make test, a limited list of tests could be run
290 # via "make test TESTS=sometests"
291 ("test", "make test FAIL_IMMEDIATELY=1 "
293 "--include-env=ktest'", "text/plain"),
294 ("install", "make install", "text/plain"),
295 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
296 ("clean", "make clean", "text/plain")
299 # Test Samba without python still builds. When this test fails
300 # due to more use of Python, the expectations is that the newly
301 # failing part of the code should be disabled when
302 # --disable-python is set (rather than major work being done to
303 # support this environment). The target here is for vendors
304 # shipping a minimal smbd.
306 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
307 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
308 ("make", "make -j", "text/plain"),
309 ("install", "make install", "text/plain"),
310 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
311 ("clean", "make clean", "text/plain"),
313 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
314 ("talloc-make", "cd lib/talloc && make", "text/plain"),
315 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
317 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
318 ("tdb-make", "cd lib/tdb && make", "text/plain"),
319 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
321 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
322 ("tevent-make", "cd lib/tevent && make", "text/plain"),
323 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
325 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
326 ("ldb-make", "cd lib/ldb && make", "text/plain"),
327 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
329 # retry against installed library packages
330 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
331 ("libs-make", "make -j", "text/plain"),
332 ("libs-install", "make install", "text/plain"),
333 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
334 ("libs-clean", "make clean", "text/plain")
340 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
341 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
342 ("make", "make", "text/plain"),
343 ("install", "make install", "text/plain"),
344 ("test", "make test", "text/plain"),
345 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
346 ("make-no-lmdb", "make", "text/plain"),
347 ("install-no-lmdb", "make install", "text/plain"),
348 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
349 ("distcheck", "make distcheck", "text/plain"),
350 ("clean", "make clean", "text/plain")],
353 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
354 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
355 ("make", "make", "text/plain"),
356 ("install", "make install", "text/plain"),
357 ("test", "make test", "text/plain"),
358 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
359 ("distcheck", "make distcheck", "text/plain"),
360 ("clean", "make clean", "text/plain")],
363 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
364 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
365 ("make", "make", "text/plain"),
366 ("install", "make install", "text/plain"),
367 ("test", "make test", "text/plain"),
368 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
369 ("distcheck", "make distcheck", "text/plain"),
370 ("clean", "make clean", "text/plain")],
373 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
374 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
375 ("make", "make", "text/plain"),
376 ("install", "make install", "text/plain"),
377 ("test", "make test", "text/plain"),
378 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
379 ("distcheck", "make distcheck", "text/plain"),
380 ("clean", "make clean", "text/plain")],
383 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
384 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
385 ("make", "make", "text/plain"),
386 ("install", "make install", "text/plain"),
387 ("test", "make test", "text/plain"),
388 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
389 ("distcheck", "make distcheck", "text/plain"),
390 ("clean", "make clean", "text/plain")],
393 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
394 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
395 ("touch", "touch *.yp", "text/plain"),
396 ("make", "make", "text/plain"),
397 ("test", "make test", "text/plain"),
398 ("install", "make install", "text/plain"),
399 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
400 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
401 ("clean", "make clean", "text/plain")],
403 "samba-buildpy3-only": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
404 ("configure", "PYTHON='python3' ./configure.developer --with-selftest-prefix=./bin/ab " + samba_configure_params, "text/plain"),
405 ("make", "PYTHON='python3' make -j", "text/plain"),
406 ("install", "PYTHON='python3' make install", "text/plain"),
407 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
408 ("clean", "PYTHON='python3' make clean", "text/plain")],
410 # these are useful for debugging autobuild
411 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
412 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
422 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
424 show = options.verbose
426 do_print("Running: '%s' in '%s'" % (cmd, dir))
428 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
430 return check_call(cmd, shell=True, cwd=dir)
432 return call(cmd, shell=True, cwd=dir)
435 class builder(object):
436 '''handle build of one directory'''
438 def __init__(self, name, sequence, cp=True, py3=False):
441 if name in builddirs:
442 self.dir = builddirs[name]
446 self.tag = self.name.replace('/', '_')
447 self.sequence = sequence
449 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
450 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
452 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
453 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
454 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
455 self.stdout = open(self.stdout_path, 'w')
456 self.stderr = open(self.stderr_path, 'w')
457 self.stdin = open("/dev/null", 'r')
458 self.sdir = "%s/%s" % (testbase, self.tag)
459 self.prefix = "%s/%s" % (test_prefix, self.tag)
460 run_cmd("rm -rf %s" % self.sdir)
461 run_cmd("rm -rf %s" % self.prefix)
463 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
465 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
468 def start_next(self):
469 if self.next == len(self.sequence):
470 if not options.nocleanup:
471 run_cmd("rm -rf %s" % self.sdir)
472 run_cmd("rm -rf %s" % self.prefix)
473 do_print('%s: Completed OK' % self.name)
476 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
477 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib=1, prefix=self.prefix))
478 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
480 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
481 # The trailing space is important
482 self.cmd = self.cmd.replace("${PY3_ONLY}", "python3 ")
484 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
485 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
486 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
487 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
488 # if self.output_mime_type == "text/x-subunit":
489 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
491 os.chdir("%s/%s" % (self.sdir, self.dir))
492 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
493 self.proc = Popen(self.cmd, shell=True,
495 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
500 class buildlist(object):
501 '''handle build of multiple directories'''
503 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
506 self.tail_proc = None
509 if options.restrict_tests:
510 tasknames = ["samba-test-only"]
512 tasknames = defaulttasks
514 # If we are only running one test,
515 # do not sleep randomly to wait for it to start
516 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
519 if n not in tasks and n.endswith("-py3"):
525 b = builder(n, tasks[n], cp=n is not "pidl")
528 rebase_remote = "rebaseon"
529 retry_task = [("retry",
531 git remote add -t %s %s %s
535 git describe %s/%s > old_remote_branch.desc
537 git describe %s/%s > remote_branch.desc
538 diff old_remote_branch.desc remote_branch.desc
541 rebase_branch, rebase_remote, rebase_url,
543 rebase_remote, rebase_branch,
545 rebase_remote, rebase_branch
549 self.retry = builder('retry', retry_task, cp=False)
550 self.need_retry = False
553 if self.tail_proc is not None:
554 self.tail_proc.terminate()
555 self.tail_proc.wait()
556 self.tail_proc = None
557 if self.retry is not None:
558 self.retry.proc.terminate()
559 self.retry.proc.wait()
562 if b.proc is not None:
563 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
575 b.status = b.proc.poll()
581 ret = self.retry.proc.poll()
583 self.need_retry = True
593 if options.retry and self.need_retry:
595 do_print("retry needed")
596 return (0, None, None, None, "retry")
599 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
601 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
604 return (0, None, None, None, "All OK")
606 def write_system_info(self):
607 filename = 'system-info.txt'
608 f = open(filename, 'w')
609 for cmd in ['uname -a', 'free', 'cat /proc/cpuinfo',
610 'cc --version', 'df -m .', 'df -m %s' % testbase]:
611 print('### %s' % cmd, file=f)
612 print(run_cmd(cmd, output=True, checkfail=False), file=f)
617 def tarlogs(self, fname):
618 tar = tarfile.open(fname, "w:gz")
620 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
621 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
622 if os.path.exists("autobuild.log"):
623 tar.add("autobuild.log")
624 sys_info = self.write_system_info()
628 def remove_logs(self):
630 os.unlink(b.stdout_path)
631 os.unlink(b.stderr_path)
633 def start_tail(self):
636 cmd.append(b.stdout_path)
637 cmd.append(b.stderr_path)
638 self.tail_proc = Popen(cmd, close_fds=True)
642 if options.nocleanup:
644 run_cmd("stat %s || true" % test_tmpdir, show=True)
645 run_cmd("stat %s" % testbase, show=True)
646 do_print("Cleaning up %r" % cleanup_list)
647 for d in cleanup_list:
648 run_cmd("rm -rf %s" % d)
652 '''get to the top of the git repo'''
655 if os.path.isdir(os.path.join(p, ".git")):
657 p = os.path.abspath(os.path.join(p, '..'))
661 def daemonize(logfile):
663 if pid == 0: # Parent
666 if pid != 0: # Actual daemon
671 import resource # Resource usage information.
672 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
673 if maxfd == resource.RLIM_INFINITY:
674 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
675 for fd in range(0, maxfd):
680 os.open(logfile, os.O_RDWR | os.O_CREAT)
685 def write_pidfile(fname):
686 '''write a pid file, cleanup on exit'''
687 f = open(fname, mode='w')
688 f.write("%u\n" % os.getpid())
692 def rebase_tree(rebase_url, rebase_branch="master"):
693 rebase_remote = "rebaseon"
694 do_print("Rebasing on %s" % rebase_url)
695 run_cmd("git describe HEAD", show=True, dir=test_master)
696 run_cmd("git remote add -t %s %s %s" %
697 (rebase_branch, rebase_remote, rebase_url),
698 show=True, dir=test_master)
699 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
700 if options.fix_whitespace:
701 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
702 (rebase_remote, rebase_branch),
703 show=True, dir=test_master)
705 run_cmd("git rebase --force-rebase %s/%s" %
706 (rebase_remote, rebase_branch),
707 show=True, dir=test_master)
708 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
709 (rebase_remote, rebase_branch),
710 dir=test_master, output=True)
712 do_print("No differences between HEAD and %s/%s - exiting" %
713 (rebase_remote, rebase_branch))
715 run_cmd("git describe %s/%s" %
716 (rebase_remote, rebase_branch),
717 show=True, dir=test_master)
718 run_cmd("git describe HEAD", show=True, dir=test_master)
719 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
720 (rebase_remote, rebase_branch),
721 show=True, dir=test_master)
724 def push_to(push_url, push_branch="master"):
725 push_remote = "pushto"
726 do_print("Pushing to %s" % push_url)
728 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
729 run_cmd("git commit --amend -c HEAD", dir=test_master)
730 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
731 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
732 run_cmd("git remote add -t %s %s %s" %
733 (push_branch, push_remote, push_url),
734 show=True, dir=test_master)
735 run_cmd("git push %s +HEAD:%s" %
736 (push_remote, push_branch),
737 show=True, dir=test_master)
740 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
742 gitroot = find_git_root()
744 raise Exception("Failed to find git root")
746 parser = OptionParser()
747 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
748 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
749 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
750 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
751 default=def_testbase)
752 parser.add_option("", "--passcmd", help="command to run on success", default=None)
753 parser.add_option("", "--verbose", help="show all commands as they are run",
754 default=False, action="store_true")
755 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
756 default=None, type='str')
757 parser.add_option("", "--pushto", help="push to a git url on success",
758 default=None, type='str')
759 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
760 default=False, action="store_true")
761 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
762 default=False, action="store_true")
763 parser.add_option("", "--retry", help="automatically retry if master changes",
764 default=False, action="store_true")
765 parser.add_option("", "--email", help="send email to the given address on failure",
766 type='str', default=None)
767 parser.add_option("", "--email-from", help="send email from the given address",
768 type='str', default="autobuild@samba.org")
769 parser.add_option("", "--email-server", help="send email via the given server",
770 type='str', default='localhost')
771 parser.add_option("", "--always-email", help="always send email, even on success",
773 parser.add_option("", "--daemon", help="daemonize after initial setup",
775 parser.add_option("", "--branch", help="the branch to work on (default=master)",
776 default="master", type='str')
777 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
778 default=gitroot, type='str')
779 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
780 default=False, action="store_true")
781 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
785 def send_email(subject, text, log_tar):
786 if options.email is None:
787 do_print("not sending email because the recipient is not set")
788 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
791 outer = MIMEMultipart()
792 outer['Subject'] = subject
793 outer['To'] = options.email
794 outer['From'] = options.email_from
795 outer['Date'] = email.utils.formatdate(localtime=True)
796 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
797 outer.attach(MIMEText(text, 'plain'))
798 if options.attach_logs:
799 fp = open(log_tar, 'rb')
800 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
802 # Set the filename parameter
803 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
805 content = outer.as_string()
806 s = smtplib.SMTP(options.email_server)
807 s.sendmail(options.email_from, [options.email], content)
812 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
813 elapsed_time, log_base=None, add_log_tail=True):
814 '''send an email to options.email about the failure'''
815 elapsed_minutes = elapsed_time / 60.0
816 user = os.getenv("USER")
822 Your autobuild on %s failed after %.1f minutes
823 when trying to test %s with the following error:
827 the autobuild has been abandoned. Please fix the error and resubmit.
829 A summary of the autobuild process is here:
832 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
834 if options.restrict_tests:
836 The build was restricted to tests matching %s\n""" % options.restrict_tests
838 if failed_task != 'rebase':
840 You can see logs of the failed task here:
845 or you can get full logs of all tasks in this job here:
849 The top commit for the tree that was built was:
853 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
856 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
857 lines = f.readlines()
858 log_tail = "".join(lines[-50:])
859 num_lines = len(lines)
861 # Also include stderr (compile failures) if < 50 lines of stdout
862 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
863 log_tail += "".join(f.readlines()[-(50 - num_lines):])
866 The last 50 lines of log messages:
872 logs = os.path.join(gitroot, 'logs.tar.gz')
873 send_email('autobuild[%s] failure on %s for task %s during %s'
874 % (options.branch, platform.node(), failed_task, failed_stage),
878 def email_success(elapsed_time, log_base=None):
879 '''send an email to options.email about a successful build'''
880 user = os.getenv("USER")
886 Your autobuild on %s has succeeded after %.1f minutes.
888 ''' % (platform.node(), elapsed_time / 60.)
890 if options.restrict_tests:
892 The build was restricted to tests matching %s\n""" % options.restrict_tests
897 you can get full logs of all tasks in this job here:
904 The top commit for the tree that was built was:
909 logs = os.path.join(gitroot, 'logs.tar.gz')
910 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
914 (options, args) = parser.parse_args()
917 if options.rebase is None:
918 raise Exception('You can only use --retry if you also rebase')
920 testbase = "%s/b%u" % (options.testbase, os.getpid())
921 test_master = "%s/master" % testbase
922 test_prefix = "%s/prefix" % testbase
923 test_tmpdir = "%s/tmp" % testbase
924 os.environ['TMPDIR'] = test_tmpdir
926 # get the top commit message, for emails
927 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
930 os.makedirs(testbase)
931 except Exception as reason:
932 raise Exception("Unable to create %s : %s" % (testbase, reason))
933 cleanup_list.append(testbase)
936 logfile = os.path.join(testbase, "log")
937 do_print("Forking into the background, writing progress to %s" % logfile)
940 write_pidfile(gitroot + "/autobuild.pid")
942 start_time = time.time()
946 run_cmd("rm -rf %s" % test_tmpdir, show=True)
947 os.makedirs(test_tmpdir)
948 # The waf uninstall code removes empty directories all the way
949 # up the tree. Creating a file in test_tmpdir stops it from
951 run_cmd("touch %s" % os.path.join(test_tmpdir,
952 ".directory-is-not-empty"), show=True)
953 run_cmd("stat %s" % test_tmpdir, show=True)
954 run_cmd("stat %s" % testbase, show=True)
955 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
962 if options.rebase is not None:
963 rebase_tree(options.rebase, rebase_branch=options.branch)
965 cleanup_list.append(gitroot + "/autobuild.pid")
967 elapsed_time = time.time() - start_time
968 email_failure(-1, 'rebase', 'rebase', 'rebase',
969 'rebase on %s failed' % options.branch,
970 elapsed_time, log_base=options.log_base)
972 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
975 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
976 if status != 0 or errstr != "retry":
983 cleanup_list.append(gitroot + "/autobuild.pid")
989 do_print("waiting for tail to flush")
992 elapsed_time = time.time() - start_time
994 if options.passcmd is not None:
995 do_print("Running passcmd: %s" % options.passcmd)
996 run_cmd(options.passcmd, dir=test_master)
997 if options.pushto is not None:
998 push_to(options.pushto, push_branch=options.branch)
999 if options.keeplogs or options.attach_logs:
1000 blist.tarlogs("logs.tar.gz")
1001 do_print("Logs in logs.tar.gz")
1002 if options.always_email:
1003 email_success(elapsed_time, log_base=options.log_base)
1009 # something failed, gather a tar of the logs
1010 blist.tarlogs("logs.tar.gz")
1012 if options.email is not None:
1013 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1014 elapsed_time, log_base=options.log_base)
1016 elapsed_minutes = elapsed_time / 60.0
1019 ####################################################################
1023 Your autobuild[%s] on %s failed after %.1f minutes
1024 when trying to test %s with the following error:
1028 the autobuild has been abandoned. Please fix the error and resubmit.
1030 ####################################################################
1032 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1036 do_print("Logs in logs.tar.gz")