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": ".",
51 "samba-ad-dc-py3": ".",
53 "samba-ad-dc-2-py3": ".",
54 "samba-systemkrb5": ".",
55 "samba-nopython": ".",
56 "samba-buildpy3-only": ".",
57 "samba-purepy3-none-env": ".",
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 "samba-purepy3-none-env": [
411 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
412 ("configure", "PYTHON='python3' ./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
413 ("make", "PYTHON='python3' make -j", "text/plain"),
414 ("test", "PYTHON='python3' make test "
415 "FAIL_IMMEDIATELY=1 "
417 "--include-env=none'",
420 # these are useful for debugging autobuild
421 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
422 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
434 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
436 show = options.verbose
438 do_print("Running: '%s' in '%s'" % (cmd, dir))
440 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
442 return check_call(cmd, shell=True, cwd=dir)
444 return call(cmd, shell=True, cwd=dir)
447 class builder(object):
448 '''handle build of one directory'''
450 def __init__(self, name, sequence, cp=True, py3=False):
453 if name in builddirs:
454 self.dir = builddirs[name]
458 self.tag = self.name.replace('/', '_')
459 self.sequence = sequence
461 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
462 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
464 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
465 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
466 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
467 self.stdout = open(self.stdout_path, 'w')
468 self.stderr = open(self.stderr_path, 'w')
469 self.stdin = open("/dev/null", 'r')
470 self.sdir = "%s/%s" % (testbase, self.tag)
471 self.prefix = "%s/%s" % (test_prefix, self.tag)
472 run_cmd("rm -rf %s" % self.sdir)
473 run_cmd("rm -rf %s" % self.prefix)
475 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
477 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
480 def start_next(self):
481 if self.next == len(self.sequence):
482 if not options.nocleanup:
483 run_cmd("rm -rf %s" % self.sdir)
484 run_cmd("rm -rf %s" % self.prefix)
485 do_print('%s: Completed OK' % self.name)
488 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
489 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib=1, prefix=self.prefix))
490 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
492 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
493 # The trailing space is important
494 self.cmd = self.cmd.replace("${PY3_ONLY}", "python3 ")
496 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
497 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
498 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
499 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
500 # if self.output_mime_type == "text/x-subunit":
501 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
503 os.chdir("%s/%s" % (self.sdir, self.dir))
504 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
505 self.proc = Popen(self.cmd, shell=True,
507 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
512 class buildlist(object):
513 '''handle build of multiple directories'''
515 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
518 self.tail_proc = None
521 if options.restrict_tests:
522 tasknames = ["samba-test-only"]
524 tasknames = defaulttasks
526 # If we are only running one test,
527 # do not sleep randomly to wait for it to start
528 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
531 if n not in tasks and n.endswith("-py3"):
537 b = builder(n, tasks[n], cp=n is not "pidl")
540 rebase_remote = "rebaseon"
541 retry_task = [("retry",
543 git remote add -t %s %s %s
547 git describe %s/%s > old_remote_branch.desc
549 git describe %s/%s > remote_branch.desc
550 diff old_remote_branch.desc remote_branch.desc
553 rebase_branch, rebase_remote, rebase_url,
555 rebase_remote, rebase_branch,
557 rebase_remote, rebase_branch
561 self.retry = builder('retry', retry_task, cp=False)
562 self.need_retry = False
565 if self.tail_proc is not None:
566 self.tail_proc.terminate()
567 self.tail_proc.wait()
568 self.tail_proc = None
569 if self.retry is not None:
570 self.retry.proc.terminate()
571 self.retry.proc.wait()
574 if b.proc is not None:
575 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
587 b.status = b.proc.poll()
593 ret = self.retry.proc.poll()
595 self.need_retry = True
605 if options.retry and self.need_retry:
607 do_print("retry needed")
608 return (0, None, None, None, "retry")
611 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
613 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
616 return (0, None, None, None, "All OK")
618 def write_system_info(self):
619 filename = 'system-info.txt'
620 f = open(filename, 'w')
621 for cmd in ['uname -a', 'free', 'cat /proc/cpuinfo',
622 'cc --version', 'df -m .', 'df -m %s' % testbase]:
623 print('### %s' % cmd, file=f)
624 print(run_cmd(cmd, output=True, checkfail=False), file=f)
629 def tarlogs(self, fname):
630 tar = tarfile.open(fname, "w:gz")
632 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
633 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
634 if os.path.exists("autobuild.log"):
635 tar.add("autobuild.log")
636 sys_info = self.write_system_info()
640 def remove_logs(self):
642 os.unlink(b.stdout_path)
643 os.unlink(b.stderr_path)
645 def start_tail(self):
648 cmd.append(b.stdout_path)
649 cmd.append(b.stderr_path)
650 self.tail_proc = Popen(cmd, close_fds=True)
654 if options.nocleanup:
656 run_cmd("stat %s || true" % test_tmpdir, show=True)
657 run_cmd("stat %s" % testbase, show=True)
658 do_print("Cleaning up %r" % cleanup_list)
659 for d in cleanup_list:
660 run_cmd("rm -rf %s" % d)
664 '''get to the top of the git repo'''
667 if os.path.isdir(os.path.join(p, ".git")):
669 p = os.path.abspath(os.path.join(p, '..'))
673 def daemonize(logfile):
675 if pid == 0: # Parent
678 if pid != 0: # Actual daemon
683 import resource # Resource usage information.
684 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
685 if maxfd == resource.RLIM_INFINITY:
686 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
687 for fd in range(0, maxfd):
692 os.open(logfile, os.O_RDWR | os.O_CREAT)
697 def write_pidfile(fname):
698 '''write a pid file, cleanup on exit'''
699 f = open(fname, mode='w')
700 f.write("%u\n" % os.getpid())
704 def rebase_tree(rebase_url, rebase_branch="master"):
705 rebase_remote = "rebaseon"
706 do_print("Rebasing on %s" % rebase_url)
707 run_cmd("git describe HEAD", show=True, dir=test_master)
708 run_cmd("git remote add -t %s %s %s" %
709 (rebase_branch, rebase_remote, rebase_url),
710 show=True, dir=test_master)
711 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
712 if options.fix_whitespace:
713 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
714 (rebase_remote, rebase_branch),
715 show=True, dir=test_master)
717 run_cmd("git rebase --force-rebase %s/%s" %
718 (rebase_remote, rebase_branch),
719 show=True, dir=test_master)
720 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
721 (rebase_remote, rebase_branch),
722 dir=test_master, output=True)
724 do_print("No differences between HEAD and %s/%s - exiting" %
725 (rebase_remote, rebase_branch))
727 run_cmd("git describe %s/%s" %
728 (rebase_remote, rebase_branch),
729 show=True, dir=test_master)
730 run_cmd("git describe HEAD", show=True, dir=test_master)
731 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
732 (rebase_remote, rebase_branch),
733 show=True, dir=test_master)
736 def push_to(push_url, push_branch="master"):
737 push_remote = "pushto"
738 do_print("Pushing to %s" % push_url)
740 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
741 run_cmd("git commit --amend -c HEAD", dir=test_master)
742 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
743 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
744 run_cmd("git remote add -t %s %s %s" %
745 (push_branch, push_remote, push_url),
746 show=True, dir=test_master)
747 run_cmd("git push %s +HEAD:%s" %
748 (push_remote, push_branch),
749 show=True, dir=test_master)
752 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
754 gitroot = find_git_root()
756 raise Exception("Failed to find git root")
758 parser = OptionParser()
759 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
760 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
761 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
762 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
763 default=def_testbase)
764 parser.add_option("", "--passcmd", help="command to run on success", default=None)
765 parser.add_option("", "--verbose", help="show all commands as they are run",
766 default=False, action="store_true")
767 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
768 default=None, type='str')
769 parser.add_option("", "--pushto", help="push to a git url on success",
770 default=None, type='str')
771 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
772 default=False, action="store_true")
773 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
774 default=False, action="store_true")
775 parser.add_option("", "--retry", help="automatically retry if master changes",
776 default=False, action="store_true")
777 parser.add_option("", "--email", help="send email to the given address on failure",
778 type='str', default=None)
779 parser.add_option("", "--email-from", help="send email from the given address",
780 type='str', default="autobuild@samba.org")
781 parser.add_option("", "--email-server", help="send email via the given server",
782 type='str', default='localhost')
783 parser.add_option("", "--always-email", help="always send email, even on success",
785 parser.add_option("", "--daemon", help="daemonize after initial setup",
787 parser.add_option("", "--branch", help="the branch to work on (default=master)",
788 default="master", type='str')
789 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
790 default=gitroot, type='str')
791 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
792 default=False, action="store_true")
793 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
797 def send_email(subject, text, log_tar):
798 if options.email is None:
799 do_print("not sending email because the recipient is not set")
800 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
803 outer = MIMEMultipart()
804 outer['Subject'] = subject
805 outer['To'] = options.email
806 outer['From'] = options.email_from
807 outer['Date'] = email.utils.formatdate(localtime=True)
808 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
809 outer.attach(MIMEText(text, 'plain'))
810 if options.attach_logs:
811 fp = open(log_tar, 'rb')
812 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
814 # Set the filename parameter
815 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
817 content = outer.as_string()
818 s = smtplib.SMTP(options.email_server)
819 s.sendmail(options.email_from, [options.email], content)
824 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
825 elapsed_time, log_base=None, add_log_tail=True):
826 '''send an email to options.email about the failure'''
827 elapsed_minutes = elapsed_time / 60.0
828 user = os.getenv("USER")
834 Your autobuild on %s failed after %.1f minutes
835 when trying to test %s with the following error:
839 the autobuild has been abandoned. Please fix the error and resubmit.
841 A summary of the autobuild process is here:
844 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
846 if options.restrict_tests:
848 The build was restricted to tests matching %s\n""" % options.restrict_tests
850 if failed_task != 'rebase':
852 You can see logs of the failed task here:
857 or you can get full logs of all tasks in this job here:
861 The top commit for the tree that was built was:
865 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
868 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
869 lines = f.readlines()
870 log_tail = "".join(lines[-50:])
871 num_lines = len(lines)
873 # Also include stderr (compile failures) if < 50 lines of stdout
874 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
875 log_tail += "".join(f.readlines()[-(50 - num_lines):])
878 The last 50 lines of log messages:
884 logs = os.path.join(gitroot, 'logs.tar.gz')
885 send_email('autobuild[%s] failure on %s for task %s during %s'
886 % (options.branch, platform.node(), failed_task, failed_stage),
890 def email_success(elapsed_time, log_base=None):
891 '''send an email to options.email about a successful build'''
892 user = os.getenv("USER")
898 Your autobuild on %s has succeeded after %.1f minutes.
900 ''' % (platform.node(), elapsed_time / 60.)
902 if options.restrict_tests:
904 The build was restricted to tests matching %s\n""" % options.restrict_tests
909 you can get full logs of all tasks in this job here:
916 The top commit for the tree that was built was:
921 logs = os.path.join(gitroot, 'logs.tar.gz')
922 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
926 (options, args) = parser.parse_args()
929 if options.rebase is None:
930 raise Exception('You can only use --retry if you also rebase')
932 testbase = "%s/b%u" % (options.testbase, os.getpid())
933 test_master = "%s/master" % testbase
934 test_prefix = "%s/prefix" % testbase
935 test_tmpdir = "%s/tmp" % testbase
936 os.environ['TMPDIR'] = test_tmpdir
938 # get the top commit message, for emails
939 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
942 os.makedirs(testbase)
943 except Exception as reason:
944 raise Exception("Unable to create %s : %s" % (testbase, reason))
945 cleanup_list.append(testbase)
948 logfile = os.path.join(testbase, "log")
949 do_print("Forking into the background, writing progress to %s" % logfile)
952 write_pidfile(gitroot + "/autobuild.pid")
954 start_time = time.time()
958 run_cmd("rm -rf %s" % test_tmpdir, show=True)
959 os.makedirs(test_tmpdir)
960 # The waf uninstall code removes empty directories all the way
961 # up the tree. Creating a file in test_tmpdir stops it from
963 run_cmd("touch %s" % os.path.join(test_tmpdir,
964 ".directory-is-not-empty"), show=True)
965 run_cmd("stat %s" % test_tmpdir, show=True)
966 run_cmd("stat %s" % testbase, show=True)
967 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
974 if options.rebase is not None:
975 rebase_tree(options.rebase, rebase_branch=options.branch)
977 cleanup_list.append(gitroot + "/autobuild.pid")
979 elapsed_time = time.time() - start_time
980 email_failure(-1, 'rebase', 'rebase', 'rebase',
981 'rebase on %s failed' % options.branch,
982 elapsed_time, log_base=options.log_base)
984 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
987 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
988 if status != 0 or errstr != "retry":
995 cleanup_list.append(gitroot + "/autobuild.pid")
1001 do_print("waiting for tail to flush")
1004 elapsed_time = time.time() - start_time
1006 if options.passcmd is not None:
1007 do_print("Running passcmd: %s" % options.passcmd)
1008 run_cmd(options.passcmd, dir=test_master)
1009 if options.pushto is not None:
1010 push_to(options.pushto, push_branch=options.branch)
1011 if options.keeplogs or options.attach_logs:
1012 blist.tarlogs("logs.tar.gz")
1013 do_print("Logs in logs.tar.gz")
1014 if options.always_email:
1015 email_success(elapsed_time, log_base=options.log_base)
1021 # something failed, gather a tar of the logs
1022 blist.tarlogs("logs.tar.gz")
1024 if options.email is not None:
1025 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1026 elapsed_time, log_base=options.log_base)
1028 elapsed_minutes = elapsed_time / 60.0
1031 ####################################################################
1035 Your autobuild[%s] on %s failed after %.1f minutes
1036 when trying to test %s with the following error:
1040 the autobuild has been abandoned. Please fix the error and resubmit.
1042 ####################################################################
1044 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1048 do_print("Logs in logs.tar.gz")