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
22 os.environ["PYTHONUNBUFFERED"] = "1"
24 # This speeds up testing remarkably.
25 os.environ['TDB_NO_FSYNC'] = '1'
35 "samba-fileserver": ".",
40 "samba-libs-py3": ".",
42 "samba-test-only": ".",
43 "samba-none-env": ".",
44 "samba-none-env-py3": ".",
46 "samba-ad-dc-py3": ".",
48 "samba-ad-dc-2-py3": ".",
49 "samba-systemkrb5": ".",
50 "samba-nopython": ".",
53 "talloc": "lib/talloc",
54 "replace": "lib/replace",
55 "tevent": "lib/tevent",
62 defaulttasks = ["ctdb",
89 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
90 defaulttasks.remove("samba-o3")
92 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
93 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
95 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
96 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
97 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
98 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
99 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
100 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
101 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs + " ${EXTRA_PYTHON}"
103 if os.environ.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
106 extra_python = "--extra-python=/usr/bin/python3"
109 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
110 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
111 ("make", "make all", "text/plain"),
112 ("install", "make install", "text/plain"),
113 ("test", "make autotest", "text/plain"),
114 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
115 ("clean", "make clean", "text/plain")],
117 # We have 'test' before 'install' because, 'test' should work without 'install (runs ad_dc_ntvfs and all the other envs)'
118 "samba": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
119 ("make", "make -j", "text/plain"),
120 ("test", "make test FAIL_IMMEDIATELY=1 "
122 "--exclude-env=none "
123 "--exclude-env=nt4_dc "
124 "--exclude-env=nt4_member "
125 "--exclude-env=ad_dc "
126 "--exclude-env=fl2003dc "
127 "--exclude-env=fl2008r2dc "
128 "--exclude-env=ad_member "
129 "--exclude-env=ad_member_idmap_rid "
130 "--exclude-env=ad_member_idmap_ad "
131 "--exclude-env=chgdcpass "
132 "--exclude-env=vampire_2000_dc "
133 "--exclude-env=fl2000dc "
134 "--exclude-env=fileserver'",
136 ("install", "make install", "text/plain"),
137 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
138 ("clean", "make clean", "text/plain")],
140 # We split out this so the isolated nt4_dc tests do not wait for ad_dc or ad_dc_ntvfs tests (which are long)
141 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
142 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
143 ("make", "make -j", "text/plain"),
144 ("test", "make test FAIL_IMMEDIATELY=1 "
146 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
147 ("install", "make install", "text/plain"),
148 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
149 ("clean", "make clean", "text/plain")],
151 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
152 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
153 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json-audit --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
154 ("make", "make -j", "text/plain"),
155 ("test", "make test FAIL_IMMEDIATELY=1 "
157 "--include-env=fileserver'", "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": [("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=ad_dc "
167 "--include-env=fl2003dc "
168 "--include-env=fl2008r2dc "
169 "--include-env=ad_member "
170 "--include-env=ad_member_idmap_rid "
171 "--include-env=ad_member_idmap_ad'", "text/plain"),
172 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
174 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
175 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
176 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
177 ("make", "make -j", "text/plain"),
178 ("test", "make test FAIL_IMMEDIATELY=1 "
180 "--include-env=chgdcpass --include-env=vampire_2000_dc --include-env=fl2000dc'", "text/plain"),
181 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
183 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
184 ("make", "make -j", "text/plain"),
185 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
187 # Test cross-compile infrastructure
188 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
189 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
190 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
191 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
192 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
193 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
194 ("compare-results", "script/compare_cc_results.py ./bin/c4che/default.cache.py ./bin-xe/c4che/default.cache.py ./bin-xa/c4che/default.cache.py", "text/plain")],
196 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
197 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
198 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
199 ("make", "make -j", "text/plain"),
200 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
202 "--include-env=ad_dc'", "text/plain"),
203 ("install", "make install", "text/plain"),
204 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
205 ("clean", "make clean", "text/plain")],
207 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
209 # make sure we have tdb around:
210 ("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"),
211 ("tdb-make", "cd lib/tdb && make", "text/plain"),
212 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
215 # build samba with cluster support (also building ctdb):
216 ("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"),
217 ("samba-make", "make", "text/plain"),
218 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
219 ("samba-install", "make install", "text/plain"),
220 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
223 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
224 ("clean", "make clean", "text/plain"),
225 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
228 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
229 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
230 ("talloc-make", "cd lib/talloc && make", "text/plain"),
231 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
233 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
234 ("tdb-make", "cd lib/tdb && make", "text/plain"),
235 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
237 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
238 ("tevent-make", "cd lib/tevent && make", "text/plain"),
239 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
241 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
242 ("ldb-make", "cd lib/ldb && make", "text/plain"),
243 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
245 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
246 ("nondevel-make", "make -j", "text/plain"),
247 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
248 ("nondevel-install", "make install", "text/plain"),
249 ("nondevel-dist", "make dist", "text/plain"),
251 # retry with all modules shared
252 ("allshared-distclean", "make distclean", "text/plain"),
253 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
254 ("allshared-make", "make -j", "text/plain")],
257 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
258 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
259 ("make", "make -j", "text/plain"),
260 ("test", "make test "
261 "FAIL_IMMEDIATELY=1 "
263 "--include-env=none'",
267 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
268 # build with all modules static
269 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
270 ("allstatic-make", "make -j", "text/plain"),
271 ("allstatic-test", "make test "
272 "FAIL_IMMEDIATELY=1 "
273 "TESTS='samba3.smb2.create.*nt4_dc'",
276 # retry without any required modules
277 ("none-distclean", "make distclean", "text/plain"),
278 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
279 ("none-make", "make -j", "text/plain"),
281 # retry with nonshared smbd and smbtorture
282 ("nonshared-distclean", "make distclean", "text/plain"),
283 ("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"),
284 ("nonshared-make", "make -j", "text/plain")],
286 "samba-systemkrb5": [
287 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
288 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
289 ("make", "make -j", "text/plain"),
290 # we currently cannot run a full make test, a limited list of tests could be run
291 # via "make test TESTS=sometests"
292 ("test", "make test FAIL_IMMEDIATELY=1 "
294 "--include-env=ktest'", "text/plain"),
295 ("install", "make install", "text/plain"),
296 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
297 ("clean", "make clean", "text/plain")
300 # Test Samba without python still builds. When this test fails
301 # due to more use of Python, the expectations is that the newly
302 # failing part of the code should be disabled when
303 # --disable-python is set (rather than major work being done to
304 # support this environment). The target here is for vendors
305 # shipping a minimal smbd.
307 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
308 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
309 ("make", "make -j", "text/plain"),
310 ("install", "make install", "text/plain"),
311 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
312 ("clean", "make clean", "text/plain"),
314 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
315 ("talloc-make", "cd lib/talloc && make", "text/plain"),
316 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
318 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
319 ("tdb-make", "cd lib/tdb && make", "text/plain"),
320 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
322 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
323 ("tevent-make", "cd lib/tevent && make", "text/plain"),
324 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
326 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
327 ("ldb-make", "cd lib/ldb && make", "text/plain"),
328 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
330 # retry against installed library packages
331 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
332 ("libs-make", "make -j", "text/plain"),
333 ("libs-install", "make install", "text/plain"),
334 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
335 ("libs-clean", "make clean", "text/plain")
341 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
342 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
343 ("make", "make", "text/plain"),
344 ("install", "make install", "text/plain"),
345 ("test", "make test", "text/plain"),
346 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
347 ("make-no-lmdb", "make", "text/plain"),
348 ("install-no-lmdb", "make install", "text/plain"),
349 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
350 ("distcheck", "make distcheck", "text/plain"),
351 ("clean", "make clean", "text/plain")],
354 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
355 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
356 ("make", "make", "text/plain"),
357 ("install", "make install", "text/plain"),
358 ("test", "make test", "text/plain"),
359 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
360 ("distcheck", "make distcheck", "text/plain"),
361 ("clean", "make clean", "text/plain")],
364 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
365 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
366 ("make", "make", "text/plain"),
367 ("install", "make install", "text/plain"),
368 ("test", "make test", "text/plain"),
369 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
370 ("distcheck", "make distcheck", "text/plain"),
371 ("clean", "make clean", "text/plain")],
374 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
375 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
376 ("make", "make", "text/plain"),
377 ("install", "make install", "text/plain"),
378 ("test", "make test", "text/plain"),
379 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
380 ("distcheck", "make distcheck", "text/plain"),
381 ("clean", "make clean", "text/plain")],
384 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
385 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
386 ("make", "make", "text/plain"),
387 ("install", "make install", "text/plain"),
388 ("test", "make test", "text/plain"),
389 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
390 ("distcheck", "make distcheck", "text/plain"),
391 ("clean", "make clean", "text/plain")],
394 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
395 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
396 ("touch", "touch *.yp", "text/plain"),
397 ("make", "make", "text/plain"),
398 ("test", "make test", "text/plain"),
399 ("install", "make install", "text/plain"),
400 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
401 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
402 ("clean", "make clean", "text/plain")],
404 # these are useful for debugging autobuild
405 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
406 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
416 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
418 show = options.verbose
420 do_print("Running: '%s' in '%s'" % (cmd, dir))
422 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir).communicate()[0]
424 return check_call(cmd, shell=True, cwd=dir)
426 return call(cmd, shell=True, cwd=dir)
429 class builder(object):
430 '''handle build of one directory'''
432 def __init__(self, name, sequence, cp=True, py3=False):
435 self.dir = builddirs[name]
437 self.tag = self.name.replace('/', '_')
438 self.sequence = sequence
440 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
441 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
443 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
444 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
445 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
446 self.stdout = open(self.stdout_path, 'w')
447 self.stderr = open(self.stderr_path, 'w')
448 self.stdin = open("/dev/null", 'r')
449 self.sdir = "%s/%s" % (testbase, self.tag)
450 self.prefix = "%s/%s" % (test_prefix, self.tag)
451 run_cmd("rm -rf %s" % self.sdir)
452 run_cmd("rm -rf %s" % self.prefix)
454 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
456 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
459 def start_next(self):
460 if self.next == len(self.sequence):
461 if not options.nocleanup:
462 run_cmd("rm -rf %s" % self.sdir)
463 run_cmd("rm -rf %s" % self.prefix)
464 do_print('%s: Completed OK' % self.name)
467 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
468 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib=1, prefix=self.prefix))
469 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
471 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
472 # The trailing space is important
473 self.cmd = self.cmd.replace("${PY3_ONLY}", "python3 ")
475 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
476 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
477 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
478 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
479 # if self.output_mime_type == "text/x-subunit":
480 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
481 do_print('%s: [%s] Running %s' % (self.name, self.stage, self.cmd))
483 os.chdir("%s/%s" % (self.sdir, self.dir))
484 self.proc = Popen(self.cmd, shell=True,
485 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
490 class buildlist(object):
491 '''handle build of multiple directories'''
493 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
496 self.tail_proc = None
499 if options.restrict_tests:
500 tasknames = ["samba-test-only"]
502 tasknames = defaulttasks
504 # If we are only running one test,
505 # do not sleep randomly to wait for it to start
506 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
509 if n not in tasks and n.endswith("-py3"):
515 b = builder(n, tasks[n], cp=n is not "pidl")
518 rebase_remote = "rebaseon"
519 retry_task = [("retry",
521 git remote add -t %s %s %s
525 git describe %s/%s > old_remote_branch.desc
527 git describe %s/%s > remote_branch.desc
528 diff old_remote_branch.desc remote_branch.desc
531 rebase_branch, rebase_remote, rebase_url,
533 rebase_remote, rebase_branch,
535 rebase_remote, rebase_branch
539 self.retry = builder('retry', retry_task, cp=False)
540 self.need_retry = False
543 if self.tail_proc is not None:
544 self.tail_proc.terminate()
545 self.tail_proc.wait()
546 self.tail_proc = None
547 if self.retry is not None:
548 self.retry.proc.terminate()
549 self.retry.proc.wait()
552 if b.proc is not None:
553 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
565 b.status = b.proc.poll()
571 ret = self.retry.proc.poll()
573 self.need_retry = True
583 if options.retry and self.need_retry:
585 do_print("retry needed")
586 return (0, None, None, None, "retry")
589 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
591 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
594 return (0, None, None, None, "All OK")
596 def write_system_info(self):
597 filename = 'system-info.txt'
598 f = open(filename, 'w')
599 for cmd in ['uname -a', 'free', 'cat /proc/cpuinfo',
600 'cc --version', 'df -m .', 'df -m %s' % testbase]:
601 print('### %s' % cmd, file=f)
602 print(run_cmd(cmd, output=True, checkfail=False), file=f)
607 def tarlogs(self, fname):
608 tar = tarfile.open(fname, "w:gz")
610 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
611 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
612 if os.path.exists("autobuild.log"):
613 tar.add("autobuild.log")
614 sys_info = self.write_system_info()
618 def remove_logs(self):
620 os.unlink(b.stdout_path)
621 os.unlink(b.stderr_path)
623 def start_tail(self):
625 cmd = "tail -f *.stdout *.stderr"
627 self.tail_proc = Popen(cmd, shell=True)
632 if options.nocleanup:
634 run_cmd("stat %s || true" % test_tmpdir, show=True)
635 run_cmd("stat %s" % testbase, show=True)
636 do_print("Cleaning up ....")
637 for d in cleanup_list:
638 run_cmd("rm -rf %s" % d)
642 '''get to the top of the git repo'''
645 if os.path.isdir(os.path.join(p, ".git")):
647 p = os.path.abspath(os.path.join(p, '..'))
651 def daemonize(logfile):
653 if pid == 0: # Parent
656 if pid != 0: # Actual daemon
661 import resource # Resource usage information.
662 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
663 if maxfd == resource.RLIM_INFINITY:
664 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
665 for fd in range(0, maxfd):
670 os.open(logfile, os.O_RDWR | os.O_CREAT)
675 def write_pidfile(fname):
676 '''write a pid file, cleanup on exit'''
677 f = open(fname, mode='w')
678 f.write("%u\n" % os.getpid())
682 def rebase_tree(rebase_url, rebase_branch="master"):
683 rebase_remote = "rebaseon"
684 do_print("Rebasing on %s" % rebase_url)
685 run_cmd("git describe HEAD", show=True, dir=test_master)
686 run_cmd("git remote add -t %s %s %s" %
687 (rebase_branch, rebase_remote, rebase_url),
688 show=True, dir=test_master)
689 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
690 if options.fix_whitespace:
691 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
692 (rebase_remote, rebase_branch),
693 show=True, dir=test_master)
695 run_cmd("git rebase --force-rebase %s/%s" %
696 (rebase_remote, rebase_branch),
697 show=True, dir=test_master)
698 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
699 (rebase_remote, rebase_branch),
700 dir=test_master, output=True)
702 do_print("No differences between HEAD and %s/%s - exiting" %
703 (rebase_remote, rebase_branch))
705 run_cmd("git describe %s/%s" %
706 (rebase_remote, rebase_branch),
707 show=True, dir=test_master)
708 run_cmd("git describe HEAD", show=True, dir=test_master)
709 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
710 (rebase_remote, rebase_branch),
711 show=True, dir=test_master)
714 def push_to(push_url, push_branch="master"):
715 push_remote = "pushto"
716 do_print("Pushing to %s" % push_url)
718 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
719 run_cmd("git commit --amend -c HEAD", dir=test_master)
720 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
721 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
722 run_cmd("git remote add -t %s %s %s" %
723 (push_branch, push_remote, push_url),
724 show=True, dir=test_master)
725 run_cmd("git push %s +HEAD:%s" %
726 (push_remote, push_branch),
727 show=True, dir=test_master)
730 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
732 gitroot = find_git_root()
734 raise Exception("Failed to find git root")
736 parser = OptionParser()
737 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
738 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
739 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
740 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
741 default=def_testbase)
742 parser.add_option("", "--passcmd", help="command to run on success", default=None)
743 parser.add_option("", "--verbose", help="show all commands as they are run",
744 default=False, action="store_true")
745 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
746 default=None, type='str')
747 parser.add_option("", "--pushto", help="push to a git url on success",
748 default=None, type='str')
749 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
750 default=False, action="store_true")
751 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
752 default=False, action="store_true")
753 parser.add_option("", "--retry", help="automatically retry if master changes",
754 default=False, action="store_true")
755 parser.add_option("", "--email", help="send email to the given address on failure",
756 type='str', default=None)
757 parser.add_option("", "--email-from", help="send email from the given address",
758 type='str', default="autobuild@samba.org")
759 parser.add_option("", "--email-server", help="send email via the given server",
760 type='str', default='localhost')
761 parser.add_option("", "--always-email", help="always send email, even on success",
763 parser.add_option("", "--daemon", help="daemonize after initial setup",
765 parser.add_option("", "--branch", help="the branch to work on (default=master)",
766 default="master", type='str')
767 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
768 default=gitroot, type='str')
769 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
770 default=False, action="store_true")
771 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
775 def send_email(subject, text, log_tar):
776 if options.email is None:
777 do_print("not sending email because the recipient is not set")
778 do_print("the text content would have been:\n\nSubject: %s\n\nTs" %
781 outer = MIMEMultipart()
782 outer['Subject'] = subject
783 outer['To'] = options.email
784 outer['From'] = options.email_from
785 outer['Date'] = email.utils.formatdate(localtime=True)
786 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
787 outer.attach(MIMEText(text, 'plain'))
788 if options.attach_logs:
789 fp = open(log_tar, 'rb')
790 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
792 # Set the filename parameter
793 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
795 content = outer.as_string()
796 s = smtplib.SMTP(options.email_server)
797 s.sendmail(options.email_from, [options.email], content)
802 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
803 elapsed_time, log_base=None, add_log_tail=True):
804 '''send an email to options.email about the failure'''
805 elapsed_minutes = elapsed_time / 60.0
806 user = os.getenv("USER")
812 Your autobuild on %s failed after %.1f minutes
813 when trying to test %s with the following error:
817 the autobuild has been abandoned. Please fix the error and resubmit.
819 A summary of the autobuild process is here:
822 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
824 if options.restrict_tests:
826 The build was restricted to tests matching %s\n""" % options.restrict_tests
828 if failed_task != 'rebase':
830 You can see logs of the failed task here:
835 or you can get full logs of all tasks in this job here:
839 The top commit for the tree that was built was:
843 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
846 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
847 lines = f.readlines()
848 log_tail = "".join(lines[-50:])
849 num_lines = len(lines)
851 # Also include stderr (compile failures) if < 50 lines of stdout
852 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
853 log_tail += "".join(f.readlines()[-(50 - num_lines):])
856 The last 50 lines of log messages:
862 logs = os.path.join(gitroot, 'logs.tar.gz')
863 send_email('autobuild[%s] failure on %s for task %s during %s'
864 % (options.branch, platform.node(), failed_task, failed_stage),
868 def email_success(elapsed_time, log_base=None):
869 '''send an email to options.email about a successful build'''
870 user = os.getenv("USER")
876 Your autobuild on %s has succeeded after %.1f minutes.
878 ''' % (platform.node(), elapsed_time / 60.)
880 if options.restrict_tests:
882 The build was restricted to tests matching %s\n""" % options.restrict_tests
887 you can get full logs of all tasks in this job here:
894 The top commit for the tree that was built was:
899 logs = os.path.join(gitroot, 'logs.tar.gz')
900 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
904 (options, args) = parser.parse_args()
907 if options.rebase is None:
908 raise Exception('You can only use --retry if you also rebase')
910 testbase = "%s/b%u" % (options.testbase, os.getpid())
911 test_master = "%s/master" % testbase
912 test_prefix = "%s/prefix" % testbase
913 test_tmpdir = "%s/tmp" % testbase
914 os.environ['TMPDIR'] = test_tmpdir
916 # get the top commit message, for emails
917 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
920 os.makedirs(testbase)
921 except Exception as reason:
922 raise Exception("Unable to create %s : %s" % (testbase, reason))
923 cleanup_list.append(testbase)
926 logfile = os.path.join(testbase, "log")
927 do_print("Forking into the background, writing progress to %s" % logfile)
930 write_pidfile(gitroot + "/autobuild.pid")
932 start_time = time.time()
936 run_cmd("rm -rf %s" % test_tmpdir, show=True)
937 os.makedirs(test_tmpdir)
938 # The waf uninstall code removes empty directories all the way
939 # up the tree. Creating a file in test_tmpdir stops it from
941 run_cmd("touch %s" % os.path.join(test_tmpdir,
942 ".directory-is-not-empty"), show=True)
943 run_cmd("stat %s" % test_tmpdir, show=True)
944 run_cmd("stat %s" % testbase, show=True)
945 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
952 if options.rebase is not None:
953 rebase_tree(options.rebase, rebase_branch=options.branch)
955 cleanup_list.append(gitroot + "/autobuild.pid")
957 elapsed_time = time.time() - start_time
958 email_failure(-1, 'rebase', 'rebase', 'rebase',
959 'rebase on %s failed' % options.branch,
960 elapsed_time, log_base=options.log_base)
962 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
965 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
966 if status != 0 or errstr != "retry":
973 cleanup_list.append(gitroot + "/autobuild.pid")
979 do_print("waiting for tail to flush")
982 elapsed_time = time.time() - start_time
984 if options.passcmd is not None:
985 do_print("Running passcmd: %s" % options.passcmd)
986 run_cmd(options.passcmd, dir=test_master)
987 if options.pushto is not None:
988 push_to(options.pushto, push_branch=options.branch)
989 if options.keeplogs or options.attach_logs:
990 blist.tarlogs("logs.tar.gz")
991 do_print("Logs in logs.tar.gz")
992 if options.always_email:
993 email_success(elapsed_time, log_base=options.log_base)
999 # something failed, gather a tar of the logs
1000 blist.tarlogs("logs.tar.gz")
1002 if options.email is not None:
1003 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1004 elapsed_time, log_base=options.log_base)
1006 elapsed_minutes = elapsed_time / 60.0
1009 ####################################################################
1013 Your autobuild[%s] on %s failed after %.1f minutes
1014 when trying to test %s with the following error:
1018 the autobuild has been abandoned. Please fix the error and resubmit.
1020 ####################################################################
1022 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1026 do_print("Logs in logs.tar.gz")