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-none-env": ".",
43 "samba-none-env-py3": ".",
45 "samba-ad-dc-py3": ".",
47 "samba-ad-dc-2-py3": ".",
48 "samba-systemkrb5": ".",
49 "samba-nopython": ".",
52 "talloc": "lib/talloc",
53 "replace": "lib/replace",
54 "tevent": "lib/tevent",
58 defaulttasks = builddirs.keys()
60 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
61 defaulttasks.remove("samba-o3")
63 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
64 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
66 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
67 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
68 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
69 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
70 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
71 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
72 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs + " ${EXTRA_PYTHON}"
74 if os.environ.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
77 extra_python = "--extra-python=/usr/bin/python3"
80 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
81 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
82 ("make", "make all", "text/plain"),
83 ("install", "make install", "text/plain"),
84 ("test", "make autotest", "text/plain"),
85 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
86 ("clean", "make clean", "text/plain")],
88 # We have 'test' before 'install' because, 'test' should work without 'install (runs ad_dc_ntvfs and all the other envs)'
89 "samba": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
90 ("make", "make -j", "text/plain"),
91 ("test", "make test FAIL_IMMEDIATELY=1 "
94 "--exclude-env=nt4_dc "
95 "--exclude-env=nt4_member "
96 "--exclude-env=ad_dc "
97 "--exclude-env=ad_dc_no_nss "
98 "--exclude-env=fl2003dc "
99 "--exclude-env=fl2008r2dc "
100 "--exclude-env=ad_member "
101 "--exclude-env=ad_member_idmap_rid "
102 "--exclude-env=ad_member_idmap_ad "
103 "--exclude-env=chgdcpass "
104 "--exclude-env=vampire_2000_dc "
105 "--exclude-env=fl2000dc "
106 "--exclude-env=fileserver "
107 "--exclude-env=backupfromdc "
108 "--exclude-env=restoredc "
109 "--exclude-env=renamedc "
110 "--exclude-env=offlinebackupdc "
111 "--exclude-env=labdc "
114 ("install", "make install", "text/plain"),
115 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
116 ("clean", "make clean", "text/plain")],
118 # We split out this so the isolated nt4_dc tests do not wait for ad_dc or ad_dc_ntvfs tests (which are long)
119 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
120 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
121 ("make", "make -j", "text/plain"),
122 ("test", "make test FAIL_IMMEDIATELY=1 "
124 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
125 ("install", "make install", "text/plain"),
126 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
127 ("clean", "make clean", "text/plain")],
129 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
130 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
131 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json-audit --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
132 ("make", "make -j", "text/plain"),
133 ("test", "make test FAIL_IMMEDIATELY=1 "
135 "--include-env=fileserver'", "text/plain"),
136 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
138 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
139 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
140 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
141 ("make", "make -j", "text/plain"),
142 ("test", "make test FAIL_IMMEDIATELY=1 "
144 "--include-env=ad_dc "
145 "--include-env=fl2003dc "
146 "--include-env=fl2008r2dc "
147 "--include-env=ad_member "
148 "--include-env=ad_member_idmap_rid "
149 "--include-env=ad_member_idmap_ad'", "text/plain"),
150 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
152 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
153 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
154 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
155 ("make", "make -j", "text/plain"),
156 ("test", "make test FAIL_IMMEDIATELY=1 "
158 "--include-env=chgdcpass "
159 "--include-env=vampire_2000_dc "
160 "--include-env=fl2000dc "
161 "--include-env=ad_dc_no_nss "
162 "--include-env=backupfromdc "
163 "--include-env=restoredc "
164 "--include-env=renamedc "
165 "--include-env=offlinebackupdc "
166 "--include-env=labdc "
169 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
171 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
172 ("make", "make -j", "text/plain"),
173 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
175 # Test cross-compile infrastructure
176 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
177 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
178 ("configure-cross-execute", "./configure.developer -b ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
179 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
180 ("configure-cross-answers", "./configure.developer -b ./bin-xa --cross-compile" \
181 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
182 ("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")],
184 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
185 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
186 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
187 ("make", "make -j", "text/plain"),
188 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
190 "--include-env=ad_dc'", "text/plain"),
191 ("install", "make install", "text/plain"),
192 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
193 ("clean", "make clean", "text/plain")],
195 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
197 # make sure we have tdb around:
198 ("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"),
199 ("tdb-make", "cd lib/tdb && make", "text/plain"),
200 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
203 # build samba with cluster support (also building ctdb):
204 ("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"),
205 ("samba-make", "make", "text/plain"),
206 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
207 ("samba-install", "make install", "text/plain"),
208 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
211 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
212 ("clean", "make clean", "text/plain"),
213 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
216 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
217 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
218 ("talloc-make", "cd lib/talloc && make", "text/plain"),
219 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
221 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
222 ("tdb-make", "cd lib/tdb && make", "text/plain"),
223 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
225 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
226 ("tevent-make", "cd lib/tevent && make", "text/plain"),
227 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
229 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
230 ("ldb-make", "cd lib/ldb && make", "text/plain"),
231 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
233 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
234 ("nondevel-make", "make -j", "text/plain"),
235 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
236 ("nondevel-install", "make install", "text/plain"),
237 ("nondevel-dist", "make dist", "text/plain"),
239 # retry with all modules shared
240 ("allshared-distclean", "make distclean", "text/plain"),
241 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
242 ("allshared-make", "make -j", "text/plain")],
245 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
246 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
247 ("make", "make -j", "text/plain"),
248 ("test", "make test "
249 "FAIL_IMMEDIATELY=1 "
251 "--include-env=none'",
255 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
256 # build with all modules static
257 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
258 ("allstatic-make", "make -j", "text/plain"),
259 ("allstatic-test", "make test "
260 "FAIL_IMMEDIATELY=1 "
261 "TESTS='samba3.smb2.create.*nt4_dc'",
264 # retry without any required modules
265 ("none-distclean", "make distclean", "text/plain"),
266 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
267 ("none-make", "make -j", "text/plain"),
269 # retry with nonshared smbd and smbtorture
270 ("nonshared-distclean", "make distclean", "text/plain"),
271 ("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"),
272 ("nonshared-make", "make -j", "text/plain")],
274 "samba-systemkrb5": [
275 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
276 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
277 ("make", "make -j", "text/plain"),
278 # we currently cannot run a full make test, a limited list of tests could be run
279 # via "make test TESTS=sometests"
280 ("test", "make test FAIL_IMMEDIATELY=1 "
282 "--include-env=ktest'", "text/plain"),
283 ("install", "make install", "text/plain"),
284 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
285 ("clean", "make clean", "text/plain")
288 # Test Samba without python still builds. When this test fails
289 # due to more use of Python, the expectations is that the newly
290 # failing part of the code should be disabled when
291 # --disable-python is set (rather than major work being done to
292 # support this environment). The target here is for vendors
293 # shipping a minimal smbd.
295 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
296 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
297 ("make", "make -j", "text/plain"),
298 ("install", "make install", "text/plain"),
299 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
300 ("clean", "make clean", "text/plain"),
302 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
303 ("talloc-make", "cd lib/talloc && make", "text/plain"),
304 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
306 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
307 ("tdb-make", "cd lib/tdb && make", "text/plain"),
308 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
310 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
311 ("tevent-make", "cd lib/tevent && make", "text/plain"),
312 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
314 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
315 ("ldb-make", "cd lib/ldb && make", "text/plain"),
316 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
318 # retry against installed library packages
319 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
320 ("libs-make", "make -j", "text/plain"),
321 ("libs-install", "make install", "text/plain"),
322 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
323 ("libs-clean", "make clean", "text/plain")
329 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
330 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
331 ("make", "make", "text/plain"),
332 ("install", "make install", "text/plain"),
333 ("test", "make test", "text/plain"),
334 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
335 ("make-no-lmdb", "make", "text/plain"),
336 ("install-no-lmdb", "make install", "text/plain"),
337 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
338 ("distcheck", "make distcheck", "text/plain"),
339 ("clean", "make clean", "text/plain")],
342 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
343 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
344 ("make", "make", "text/plain"),
345 ("install", "make install", "text/plain"),
346 ("test", "make test", "text/plain"),
347 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
348 ("distcheck", "make distcheck", "text/plain"),
349 ("clean", "make clean", "text/plain")],
352 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
353 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
354 ("make", "make", "text/plain"),
355 ("install", "make install", "text/plain"),
356 ("test", "make test", "text/plain"),
357 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
358 ("distcheck", "make distcheck", "text/plain"),
359 ("clean", "make clean", "text/plain")],
362 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
363 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
364 ("make", "make", "text/plain"),
365 ("install", "make install", "text/plain"),
366 ("test", "make test", "text/plain"),
367 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
368 ("distcheck", "make distcheck", "text/plain"),
369 ("clean", "make clean", "text/plain")],
372 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
373 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
374 ("make", "make", "text/plain"),
375 ("install", "make install", "text/plain"),
376 ("test", "make test", "text/plain"),
377 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
378 ("distcheck", "make distcheck", "text/plain"),
379 ("clean", "make clean", "text/plain")],
382 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
383 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
384 ("touch", "touch *.yp", "text/plain"),
385 ("make", "make", "text/plain"),
386 ("test", "make test", "text/plain"),
387 ("install", "make install", "text/plain"),
388 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
389 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
390 ("clean", "make clean", "text/plain")],
392 # these are useful for debugging autobuild
393 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
394 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
404 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
406 show = options.verbose
408 do_print("Running: '%s' in '%s'" % (cmd, dir))
410 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir).communicate()[0]
412 return check_call(cmd, shell=True, cwd=dir)
414 return call(cmd, shell=True, cwd=dir)
417 class builder(object):
418 '''handle build of one directory'''
420 def __init__(self, name, sequence, cp=True, py3=False):
423 if name in builddirs:
424 self.dir = builddirs[name]
428 self.tag = self.name.replace('/', '_')
429 self.sequence = sequence
431 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
432 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
434 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
435 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
436 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
437 self.stdout = open(self.stdout_path, 'w')
438 self.stderr = open(self.stderr_path, 'w')
439 self.stdin = open("/dev/null", 'r')
440 self.sdir = "%s/%s" % (testbase, self.tag)
441 self.prefix = "%s/%s" % (test_prefix, self.tag)
442 run_cmd("rm -rf %s" % self.sdir)
443 run_cmd("rm -rf %s" % self.prefix)
445 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
447 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
450 def start_next(self):
451 if self.next == len(self.sequence):
452 if not options.nocleanup:
453 run_cmd("rm -rf %s" % self.sdir)
454 run_cmd("rm -rf %s" % self.prefix)
455 do_print('%s: Completed OK' % self.name)
458 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
459 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib=1, prefix=self.prefix))
460 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
462 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
463 # The trailing space is important
464 self.cmd = self.cmd.replace("${PY3_ONLY}", "python3 ")
466 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
467 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
468 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
469 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
470 # if self.output_mime_type == "text/x-subunit":
471 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
472 do_print('%s: [%s] Running %s' % (self.name, self.stage, self.cmd))
474 os.chdir("%s/%s" % (self.sdir, self.dir))
475 self.proc = Popen(self.cmd, shell=True,
476 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
481 class buildlist(object):
482 '''handle build of multiple directories'''
484 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
487 self.tail_proc = None
490 if options.restrict_tests:
491 tasknames = ["samba-test-only"]
493 tasknames = defaulttasks
495 # If we are only running one test,
496 # do not sleep randomly to wait for it to start
497 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
500 if n not in tasks and n.endswith("-py3"):
506 b = builder(n, tasks[n], cp=n is not "pidl")
509 rebase_remote = "rebaseon"
510 retry_task = [("retry",
512 git remote add -t %s %s %s
516 git describe %s/%s > old_remote_branch.desc
518 git describe %s/%s > remote_branch.desc
519 diff old_remote_branch.desc remote_branch.desc
522 rebase_branch, rebase_remote, rebase_url,
524 rebase_remote, rebase_branch,
526 rebase_remote, rebase_branch
530 self.retry = builder('retry', retry_task, cp=False)
531 self.need_retry = False
534 if self.tail_proc is not None:
535 self.tail_proc.terminate()
536 self.tail_proc.wait()
537 self.tail_proc = None
538 if self.retry is not None:
539 self.retry.proc.terminate()
540 self.retry.proc.wait()
543 if b.proc is not None:
544 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
556 b.status = b.proc.poll()
562 ret = self.retry.proc.poll()
564 self.need_retry = True
574 if options.retry and self.need_retry:
576 do_print("retry needed")
577 return (0, None, None, None, "retry")
580 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
582 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
585 return (0, None, None, None, "All OK")
587 def write_system_info(self):
588 filename = 'system-info.txt'
589 f = open(filename, 'w')
590 for cmd in ['uname -a', 'free', 'cat /proc/cpuinfo',
591 'cc --version', 'df -m .', 'df -m %s' % testbase]:
592 print('### %s' % cmd, file=f)
593 print(run_cmd(cmd, output=True, checkfail=False), file=f)
598 def tarlogs(self, fname):
599 tar = tarfile.open(fname, "w:gz")
601 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
602 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
603 if os.path.exists("autobuild.log"):
604 tar.add("autobuild.log")
605 sys_info = self.write_system_info()
609 def remove_logs(self):
611 os.unlink(b.stdout_path)
612 os.unlink(b.stderr_path)
614 def start_tail(self):
616 cmd = "tail -f *.stdout *.stderr"
618 self.tail_proc = Popen(cmd, shell=True)
623 if options.nocleanup:
625 run_cmd("stat %s || true" % test_tmpdir, show=True)
626 run_cmd("stat %s" % testbase, show=True)
627 do_print("Cleaning up ....")
628 for d in cleanup_list:
629 run_cmd("rm -rf %s" % d)
633 '''get to the top of the git repo'''
636 if os.path.isdir(os.path.join(p, ".git")):
638 p = os.path.abspath(os.path.join(p, '..'))
642 def daemonize(logfile):
644 if pid == 0: # Parent
647 if pid != 0: # Actual daemon
652 import resource # Resource usage information.
653 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
654 if maxfd == resource.RLIM_INFINITY:
655 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
656 for fd in range(0, maxfd):
661 os.open(logfile, os.O_RDWR | os.O_CREAT)
666 def write_pidfile(fname):
667 '''write a pid file, cleanup on exit'''
668 f = open(fname, mode='w')
669 f.write("%u\n" % os.getpid())
673 def rebase_tree(rebase_url, rebase_branch="master"):
674 rebase_remote = "rebaseon"
675 do_print("Rebasing on %s" % rebase_url)
676 run_cmd("git describe HEAD", show=True, dir=test_master)
677 run_cmd("git remote add -t %s %s %s" %
678 (rebase_branch, rebase_remote, rebase_url),
679 show=True, dir=test_master)
680 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
681 if options.fix_whitespace:
682 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
683 (rebase_remote, rebase_branch),
684 show=True, dir=test_master)
686 run_cmd("git rebase --force-rebase %s/%s" %
687 (rebase_remote, rebase_branch),
688 show=True, dir=test_master)
689 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
690 (rebase_remote, rebase_branch),
691 dir=test_master, output=True)
693 do_print("No differences between HEAD and %s/%s - exiting" %
694 (rebase_remote, rebase_branch))
696 run_cmd("git describe %s/%s" %
697 (rebase_remote, rebase_branch),
698 show=True, dir=test_master)
699 run_cmd("git describe HEAD", show=True, dir=test_master)
700 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
701 (rebase_remote, rebase_branch),
702 show=True, dir=test_master)
705 def push_to(push_url, push_branch="master"):
706 push_remote = "pushto"
707 do_print("Pushing to %s" % push_url)
709 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
710 run_cmd("git commit --amend -c HEAD", dir=test_master)
711 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
712 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
713 run_cmd("git remote add -t %s %s %s" %
714 (push_branch, push_remote, push_url),
715 show=True, dir=test_master)
716 run_cmd("git push %s +HEAD:%s" %
717 (push_remote, push_branch),
718 show=True, dir=test_master)
721 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
723 gitroot = find_git_root()
725 raise Exception("Failed to find git root")
727 parser = OptionParser()
728 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
729 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
730 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
731 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
732 default=def_testbase)
733 parser.add_option("", "--passcmd", help="command to run on success", default=None)
734 parser.add_option("", "--verbose", help="show all commands as they are run",
735 default=False, action="store_true")
736 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
737 default=None, type='str')
738 parser.add_option("", "--pushto", help="push to a git url on success",
739 default=None, type='str')
740 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
741 default=False, action="store_true")
742 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
743 default=False, action="store_true")
744 parser.add_option("", "--retry", help="automatically retry if master changes",
745 default=False, action="store_true")
746 parser.add_option("", "--email", help="send email to the given address on failure",
747 type='str', default=None)
748 parser.add_option("", "--email-from", help="send email from the given address",
749 type='str', default="autobuild@samba.org")
750 parser.add_option("", "--email-server", help="send email via the given server",
751 type='str', default='localhost')
752 parser.add_option("", "--always-email", help="always send email, even on success",
754 parser.add_option("", "--daemon", help="daemonize after initial setup",
756 parser.add_option("", "--branch", help="the branch to work on (default=master)",
757 default="master", type='str')
758 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
759 default=gitroot, type='str')
760 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
761 default=False, action="store_true")
762 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
766 def send_email(subject, text, log_tar):
767 if options.email is None:
768 do_print("not sending email because the recipient is not set")
769 do_print("the text content would have been:\n\nSubject: %s\n\nTs" %
772 outer = MIMEMultipart()
773 outer['Subject'] = subject
774 outer['To'] = options.email
775 outer['From'] = options.email_from
776 outer['Date'] = email.utils.formatdate(localtime=True)
777 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
778 outer.attach(MIMEText(text, 'plain'))
779 if options.attach_logs:
780 fp = open(log_tar, 'rb')
781 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
783 # Set the filename parameter
784 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
786 content = outer.as_string()
787 s = smtplib.SMTP(options.email_server)
788 s.sendmail(options.email_from, [options.email], content)
793 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
794 elapsed_time, log_base=None, add_log_tail=True):
795 '''send an email to options.email about the failure'''
796 elapsed_minutes = elapsed_time / 60.0
797 user = os.getenv("USER")
803 Your autobuild on %s failed after %.1f minutes
804 when trying to test %s with the following error:
808 the autobuild has been abandoned. Please fix the error and resubmit.
810 A summary of the autobuild process is here:
813 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
815 if options.restrict_tests:
817 The build was restricted to tests matching %s\n""" % options.restrict_tests
819 if failed_task != 'rebase':
821 You can see logs of the failed task here:
826 or you can get full logs of all tasks in this job here:
830 The top commit for the tree that was built was:
834 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
837 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
838 lines = f.readlines()
839 log_tail = "".join(lines[-50:])
840 num_lines = len(lines)
842 # Also include stderr (compile failures) if < 50 lines of stdout
843 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
844 log_tail += "".join(f.readlines()[-(50 - num_lines):])
847 The last 50 lines of log messages:
853 logs = os.path.join(gitroot, 'logs.tar.gz')
854 send_email('autobuild[%s] failure on %s for task %s during %s'
855 % (options.branch, platform.node(), failed_task, failed_stage),
859 def email_success(elapsed_time, log_base=None):
860 '''send an email to options.email about a successful build'''
861 user = os.getenv("USER")
867 Your autobuild on %s has succeeded after %.1f minutes.
869 ''' % (platform.node(), elapsed_time / 60.)
871 if options.restrict_tests:
873 The build was restricted to tests matching %s\n""" % options.restrict_tests
878 you can get full logs of all tasks in this job here:
885 The top commit for the tree that was built was:
890 logs = os.path.join(gitroot, 'logs.tar.gz')
891 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
895 (options, args) = parser.parse_args()
898 if options.rebase is None:
899 raise Exception('You can only use --retry if you also rebase')
901 testbase = "%s/b%u" % (options.testbase, os.getpid())
902 test_master = "%s/master" % testbase
903 test_prefix = "%s/prefix" % testbase
904 test_tmpdir = "%s/tmp" % testbase
905 os.environ['TMPDIR'] = test_tmpdir
907 # get the top commit message, for emails
908 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
911 os.makedirs(testbase)
912 except Exception as reason:
913 raise Exception("Unable to create %s : %s" % (testbase, reason))
914 cleanup_list.append(testbase)
917 logfile = os.path.join(testbase, "log")
918 do_print("Forking into the background, writing progress to %s" % logfile)
921 write_pidfile(gitroot + "/autobuild.pid")
923 start_time = time.time()
927 run_cmd("rm -rf %s" % test_tmpdir, show=True)
928 os.makedirs(test_tmpdir)
929 # The waf uninstall code removes empty directories all the way
930 # up the tree. Creating a file in test_tmpdir stops it from
932 run_cmd("touch %s" % os.path.join(test_tmpdir,
933 ".directory-is-not-empty"), show=True)
934 run_cmd("stat %s" % test_tmpdir, show=True)
935 run_cmd("stat %s" % testbase, show=True)
936 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
943 if options.rebase is not None:
944 rebase_tree(options.rebase, rebase_branch=options.branch)
946 cleanup_list.append(gitroot + "/autobuild.pid")
948 elapsed_time = time.time() - start_time
949 email_failure(-1, 'rebase', 'rebase', 'rebase',
950 'rebase on %s failed' % options.branch,
951 elapsed_time, log_base=options.log_base)
953 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
956 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
957 if status != 0 or errstr != "retry":
964 cleanup_list.append(gitroot + "/autobuild.pid")
970 do_print("waiting for tail to flush")
973 elapsed_time = time.time() - start_time
975 if options.passcmd is not None:
976 do_print("Running passcmd: %s" % options.passcmd)
977 run_cmd(options.passcmd, dir=test_master)
978 if options.pushto is not None:
979 push_to(options.pushto, push_branch=options.branch)
980 if options.keeplogs or options.attach_logs:
981 blist.tarlogs("logs.tar.gz")
982 do_print("Logs in logs.tar.gz")
983 if options.always_email:
984 email_success(elapsed_time, log_base=options.log_base)
990 # something failed, gather a tar of the logs
991 blist.tarlogs("logs.tar.gz")
993 if options.email is not None:
994 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
995 elapsed_time, log_base=options.log_base)
997 elapsed_minutes = elapsed_time / 60.0
1000 ####################################################################
1004 Your autobuild[%s] on %s failed after %.1f minutes
1005 when trying to test %s with the following error:
1009 the autobuild has been abandoned. Please fix the error and resubmit.
1011 ####################################################################
1013 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1017 do_print("Logs in logs.tar.gz")