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-py2": ".",
49 "samba-none-env": ".",
51 "samba-ad-dc-py2": ".",
53 "samba-ad-dc-2-py2": ".",
54 "samba-ad-dc-backup": ".",
55 "samba-systemkrb5": ".",
56 "samba-nopython": ".",
57 "samba-buildpy2-only": ".",
60 "talloc": "lib/talloc",
61 "replace": "lib/replace",
62 "tevent": "lib/tevent",
66 defaulttasks = builddirs.keys()
68 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
69 defaulttasks.remove("samba-o3")
71 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
72 samba_configure_params = " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
74 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$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/python2"
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 "
172 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
174 # run the backup/restore testenvs separately as they're fairly standalone
175 # (and CI seems to max out at ~8 different DCs running at once)
176 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
177 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
178 ("make", "make -j", "text/plain"),
179 ("test", "make test FAIL_IMMEDIATELY=1 "
181 "--include-env=backupfromdc "
182 "--include-env=restoredc "
183 "--include-env=renamedc "
184 "--include-env=offlinebackupdc "
185 "--include-env=labdc "
188 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
190 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
191 ("make", "make -j", "text/plain"),
192 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
194 # Test cross-compile infrastructure
195 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
196 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
197 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
198 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
199 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
200 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
201 ("compare-results", "script/compare_cc_results.py "
202 "./bin/c4che/default{} "
203 "./bin-xe/c4che/default{} "
204 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
206 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
207 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
208 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
209 ("make", "make -j", "text/plain"),
210 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
212 "--include-env=ad_dc'", "text/plain"),
213 ("install", "make install", "text/plain"),
214 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
215 ("clean", "make clean", "text/plain")],
217 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
219 # make sure we have tdb around:
220 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
221 ("tdb-make", "cd lib/tdb && make", "text/plain"),
222 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
225 # build samba with cluster support (also building ctdb):
226 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure.developer --picky-developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-cluster-support --bundled-libraries=!tdb", "text/plain"),
227 ("samba-make", "make", "text/plain"),
228 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
229 ("samba-install", "make install", "text/plain"),
230 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
233 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
234 ("clean", "make clean", "text/plain"),
235 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
238 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
239 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
240 ("talloc-make", "cd lib/talloc && make", "text/plain"),
241 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
243 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
244 ("tdb-make", "cd lib/tdb && make", "text/plain"),
245 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
247 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
248 ("tevent-make", "cd lib/tevent && make", "text/plain"),
249 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
251 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
252 ("ldb-make", "cd lib/ldb && make", "text/plain"),
253 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
255 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
256 ("nondevel-make", "make -j", "text/plain"),
257 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
258 ("nondevel-install", "make install", "text/plain"),
259 ("nondevel-dist", "make dist", "text/plain"),
261 # retry with all modules shared
262 ("allshared-distclean", "make distclean", "text/plain"),
263 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
264 ("allshared-make", "make -j", "text/plain")],
267 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
268 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
269 ("make", "make -j", "text/plain"),
270 ("test", "make test "
271 "FAIL_IMMEDIATELY=1 "
273 "--include-env=none'",
277 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
278 # build with all modules static
279 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
280 ("allstatic-make", "make -j", "text/plain"),
281 ("allstatic-test", "make test "
282 "FAIL_IMMEDIATELY=1 "
283 "TESTS='samba3.smb2.create.*nt4_dc'",
286 # retry without any required modules
287 ("none-distclean", "make distclean", "text/plain"),
288 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
289 ("none-make", "make -j", "text/plain"),
291 # retry with nonshared smbd and smbtorture
292 ("nonshared-distclean", "make distclean", "text/plain"),
293 ("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"),
294 ("nonshared-make", "make -j", "text/plain")],
296 "samba-systemkrb5": [
297 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
298 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
299 ("make", "make -j", "text/plain"),
300 # we currently cannot run a full make test, a limited list of tests could be run
301 # via "make test TESTS=sometests"
302 ("test", "make test FAIL_IMMEDIATELY=1 "
304 "--include-env=ktest'", "text/plain"),
305 ("install", "make install", "text/plain"),
306 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
307 ("clean", "make clean", "text/plain")
310 # Test Samba without python still builds. When this test fails
311 # due to more use of Python, the expectations is that the newly
312 # failing part of the code should be disabled when
313 # --disable-python is set (rather than major work being done to
314 # support this environment). The target here is for vendors
315 # shipping a minimal smbd.
317 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
318 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
319 ("make", "make -j", "text/plain"),
320 ("install", "make install", "text/plain"),
321 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
322 ("clean", "make clean", "text/plain"),
324 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
325 ("talloc-make", "cd lib/talloc && make", "text/plain"),
326 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
328 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
329 ("tdb-make", "cd lib/tdb && make", "text/plain"),
330 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
332 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
333 ("tevent-make", "cd lib/tevent && make", "text/plain"),
334 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
336 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
337 ("ldb-make", "cd lib/ldb && make", "text/plain"),
338 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
340 # retry against installed library packages
341 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
342 ("libs-make", "make -j", "text/plain"),
343 ("libs-install", "make install", "text/plain"),
344 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
345 ("libs-clean", "make clean", "text/plain")
351 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
352 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
353 ("make", "make", "text/plain"),
354 ("install", "make install", "text/plain"),
355 ("test", "make test", "text/plain"),
356 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
357 ("make-no-lmdb", "make", "text/plain"),
358 ("install-no-lmdb", "make install", "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} ${EXTRA_PYTHON}", "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}", "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", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
396 ("make", "make", "text/plain"),
397 ("install", "make install", "text/plain"),
398 ("test", "make test", "text/plain"),
399 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
400 ("distcheck", "make distcheck", "text/plain"),
401 ("clean", "make clean", "text/plain")],
404 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
405 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
406 ("touch", "touch *.yp", "text/plain"),
407 ("make", "make", "text/plain"),
408 ("test", "make test", "text/plain"),
409 ("install", "make install", "text/plain"),
410 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
411 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
412 ("clean", "make clean", "text/plain")],
414 "samba-buildpy2-only": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
415 ("configure", "PYTHON='python' ./configure.developer --with-selftest-prefix=./bin/ab " + samba_configure_params, "text/plain"),
416 ("make", "PYTHON='python' make -j", "text/plain"),
417 ("install", "PYTHON='python' make install", "text/plain"),
418 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
419 ("clean", "PYTHON='python' make clean", "text/plain")],
422 # these are useful for debugging autobuild
423 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
424 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
436 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
438 show = options.verbose
440 do_print("Running: '%s' in '%s'" % (cmd, dir))
442 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
444 return check_call(cmd, shell=True, cwd=dir)
446 return call(cmd, shell=True, cwd=dir)
449 class builder(object):
450 '''handle build of one directory'''
452 def __init__(self, name, sequence, cp=True, py3=False):
455 if name in builddirs:
456 self.dir = builddirs[name]
460 self.tag = self.name.replace('/', '_')
461 self.sequence = sequence
463 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
464 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
466 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
467 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
468 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
469 self.stdout = open(self.stdout_path, 'w')
470 self.stderr = open(self.stderr_path, 'w')
471 self.stdin = open("/dev/null", 'r')
472 self.sdir = "%s/%s" % (testbase, self.tag)
473 self.prefix = "%s/%s" % (test_prefix, self.tag)
474 run_cmd("rm -rf %s" % self.sdir)
475 run_cmd("rm -rf %s" % self.prefix)
477 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
479 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
482 def start_next(self):
483 if self.next == len(self.sequence):
484 if not options.nocleanup:
485 run_cmd("rm -rf %s" % self.sdir)
486 run_cmd("rm -rf %s" % self.prefix)
487 do_print('%s: Completed OK' % self.name)
490 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
491 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
492 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
494 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "%s" % extra_python)
495 # The trailing space is important
496 self.cmd = self.cmd.replace("${PY3_ONLY}", "python2 ")
498 self.cmd = self.cmd.replace("${EXTRA_PYTHON}", "")
499 self.cmd = self.cmd.replace("${PY3_ONLY}", "")
500 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
501 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
502 # if self.output_mime_type == "text/x-subunit":
503 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
505 os.chdir("%s/%s" % (self.sdir, self.dir))
506 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
507 self.proc = Popen(self.cmd, shell=True,
509 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
514 class buildlist(object):
515 '''handle build of multiple directories'''
517 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
520 self.tail_proc = None
523 if options.restrict_tests:
524 tasknames = ["samba-test-only"]
526 tasknames = defaulttasks
528 # If we are only running one test,
529 # do not sleep randomly to wait for it to start
530 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
533 if n not in tasks and n.endswith("-py2"):
539 b = builder(n, tasks[n], cp=n is not "pidl")
542 rebase_remote = "rebaseon"
543 retry_task = [("retry",
545 git remote add -t %s %s %s
549 git describe %s/%s > old_remote_branch.desc
551 git describe %s/%s > remote_branch.desc
552 diff old_remote_branch.desc remote_branch.desc
555 rebase_branch, rebase_remote, rebase_url,
557 rebase_remote, rebase_branch,
559 rebase_remote, rebase_branch
563 self.retry = builder('retry', retry_task, cp=False)
564 self.need_retry = False
567 if self.tail_proc is not None:
568 self.tail_proc.terminate()
569 self.tail_proc.wait()
570 self.tail_proc = None
571 if self.retry is not None:
572 self.retry.proc.terminate()
573 self.retry.proc.wait()
576 if b.proc is not None:
577 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
589 b.status = b.proc.poll()
595 ret = self.retry.proc.poll()
597 self.need_retry = True
607 if options.retry and self.need_retry:
609 do_print("retry needed")
610 return (0, None, None, None, "retry")
613 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
615 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
618 return (0, None, None, None, "All OK")
620 def write_system_info(self):
621 filename = 'system-info.txt'
622 f = open(filename, 'w')
623 for cmd in ['uname -a',
628 'df -m %s' % testbase]:
629 out = run_cmd(cmd, output=True, checkfail=False)
630 print('### %s' % cmd, file=f)
631 print(out.decode('utf8', 'backslashreplace'), file=f)
636 def tarlogs(self, fname):
637 tar = tarfile.open(fname, "w:gz")
639 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
640 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
641 if os.path.exists("autobuild.log"):
642 tar.add("autobuild.log")
643 sys_info = self.write_system_info()
647 def remove_logs(self):
649 os.unlink(b.stdout_path)
650 os.unlink(b.stderr_path)
652 def start_tail(self):
655 cmd.append(b.stdout_path)
656 cmd.append(b.stderr_path)
657 self.tail_proc = Popen(cmd, close_fds=True)
661 if options.nocleanup:
663 run_cmd("stat %s || true" % test_tmpdir, show=True)
664 run_cmd("stat %s" % testbase, show=True)
665 do_print("Cleaning up %r" % cleanup_list)
666 for d in cleanup_list:
667 run_cmd("rm -rf %s" % d)
671 '''get to the top of the git repo'''
674 if os.path.isdir(os.path.join(p, ".git")):
676 p = os.path.abspath(os.path.join(p, '..'))
680 def daemonize(logfile):
682 if pid == 0: # Parent
685 if pid != 0: # Actual daemon
690 import resource # Resource usage information.
691 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
692 if maxfd == resource.RLIM_INFINITY:
693 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
694 for fd in range(0, maxfd):
699 os.open(logfile, os.O_RDWR | os.O_CREAT)
704 def write_pidfile(fname):
705 '''write a pid file, cleanup on exit'''
706 f = open(fname, mode='w')
707 f.write("%u\n" % os.getpid())
711 def rebase_tree(rebase_url, rebase_branch="master"):
712 rebase_remote = "rebaseon"
713 do_print("Rebasing on %s" % rebase_url)
714 run_cmd("git describe HEAD", show=True, dir=test_master)
715 run_cmd("git remote add -t %s %s %s" %
716 (rebase_branch, rebase_remote, rebase_url),
717 show=True, dir=test_master)
718 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
719 if options.fix_whitespace:
720 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
721 (rebase_remote, rebase_branch),
722 show=True, dir=test_master)
724 run_cmd("git rebase --force-rebase %s/%s" %
725 (rebase_remote, rebase_branch),
726 show=True, dir=test_master)
727 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
728 (rebase_remote, rebase_branch),
729 dir=test_master, output=True)
731 do_print("No differences between HEAD and %s/%s - exiting" %
732 (rebase_remote, rebase_branch))
734 run_cmd("git describe %s/%s" %
735 (rebase_remote, rebase_branch),
736 show=True, dir=test_master)
737 run_cmd("git describe HEAD", show=True, dir=test_master)
738 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
739 (rebase_remote, rebase_branch),
740 show=True, dir=test_master)
743 def push_to(push_url, push_branch="master"):
744 push_remote = "pushto"
745 do_print("Pushing to %s" % push_url)
747 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
748 run_cmd("git commit --amend -c HEAD", dir=test_master)
749 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
750 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
751 run_cmd("git remote add -t %s %s %s" %
752 (push_branch, push_remote, push_url),
753 show=True, dir=test_master)
754 run_cmd("git push %s +HEAD:%s" %
755 (push_remote, push_branch),
756 show=True, dir=test_master)
759 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
761 gitroot = find_git_root()
763 raise Exception("Failed to find git root")
765 parser = OptionParser()
766 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
767 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
768 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
769 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
770 default=def_testbase)
771 parser.add_option("", "--passcmd", help="command to run on success", default=None)
772 parser.add_option("", "--verbose", help="show all commands as they are run",
773 default=False, action="store_true")
774 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
775 default=None, type='str')
776 parser.add_option("", "--pushto", help="push to a git url on success",
777 default=None, type='str')
778 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
779 default=False, action="store_true")
780 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
781 default=False, action="store_true")
782 parser.add_option("", "--retry", help="automatically retry if master changes",
783 default=False, action="store_true")
784 parser.add_option("", "--email", help="send email to the given address on failure",
785 type='str', default=None)
786 parser.add_option("", "--email-from", help="send email from the given address",
787 type='str', default="autobuild@samba.org")
788 parser.add_option("", "--email-server", help="send email via the given server",
789 type='str', default='localhost')
790 parser.add_option("", "--always-email", help="always send email, even on success",
792 parser.add_option("", "--daemon", help="daemonize after initial setup",
794 parser.add_option("", "--branch", help="the branch to work on (default=master)",
795 default="master", type='str')
796 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
797 default=gitroot, type='str')
798 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
799 default=False, action="store_true")
800 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
804 def send_email(subject, text, log_tar):
805 if options.email is None:
806 do_print("not sending email because the recipient is not set")
807 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
810 outer = MIMEMultipart()
811 outer['Subject'] = subject
812 outer['To'] = options.email
813 outer['From'] = options.email_from
814 outer['Date'] = email.utils.formatdate(localtime=True)
815 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
816 outer.attach(MIMEText(text, 'plain'))
817 if options.attach_logs:
818 fp = open(log_tar, 'rb')
819 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
821 # Set the filename parameter
822 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
824 content = outer.as_string()
825 s = smtplib.SMTP(options.email_server)
826 s.sendmail(options.email_from, [options.email], content)
831 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
832 elapsed_time, log_base=None, add_log_tail=True):
833 '''send an email to options.email about the failure'''
834 elapsed_minutes = elapsed_time / 60.0
840 Your autobuild on %s failed after %.1f minutes
841 when trying to test %s with the following error:
845 the autobuild has been abandoned. Please fix the error and resubmit.
847 A summary of the autobuild process is here:
850 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
852 if options.restrict_tests:
854 The build was restricted to tests matching %s\n""" % options.restrict_tests
856 if failed_task != 'rebase':
858 You can see logs of the failed task here:
863 or you can get full logs of all tasks in this job here:
867 The top commit for the tree that was built was:
871 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
874 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
875 lines = f.readlines()
876 log_tail = "".join(lines[-50:])
877 num_lines = len(lines)
879 # Also include stderr (compile failures) if < 50 lines of stdout
880 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
881 log_tail += "".join(f.readlines()[-(50 - num_lines):])
884 The last 50 lines of log messages:
890 logs = os.path.join(gitroot, 'logs.tar.gz')
891 send_email('autobuild[%s] failure on %s for task %s during %s'
892 % (options.branch, platform.node(), failed_task, failed_stage),
896 def email_success(elapsed_time, log_base=None):
897 '''send an email to options.email about a successful build'''
903 Your autobuild on %s has succeeded after %.1f minutes.
905 ''' % (platform.node(), elapsed_time / 60.)
907 if options.restrict_tests:
909 The build was restricted to tests matching %s\n""" % options.restrict_tests
914 you can get full logs of all tasks in this job here:
921 The top commit for the tree that was built was:
926 logs = os.path.join(gitroot, 'logs.tar.gz')
927 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
931 (options, args) = parser.parse_args()
934 if options.rebase is None:
935 raise Exception('You can only use --retry if you also rebase')
937 testbase = "%s/b%u" % (options.testbase, os.getpid())
938 test_master = "%s/master" % testbase
939 test_prefix = "%s/prefix" % testbase
940 test_tmpdir = "%s/tmp" % testbase
941 os.environ['TMPDIR'] = test_tmpdir
943 # get the top commit message, for emails
944 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
945 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
948 os.makedirs(testbase)
949 except Exception as reason:
950 raise Exception("Unable to create %s : %s" % (testbase, reason))
951 cleanup_list.append(testbase)
954 logfile = os.path.join(testbase, "log")
955 do_print("Forking into the background, writing progress to %s" % logfile)
958 write_pidfile(gitroot + "/autobuild.pid")
960 start_time = time.time()
964 run_cmd("rm -rf %s" % test_tmpdir, show=True)
965 os.makedirs(test_tmpdir)
966 # The waf uninstall code removes empty directories all the way
967 # up the tree. Creating a file in test_tmpdir stops it from
969 run_cmd("touch %s" % os.path.join(test_tmpdir,
970 ".directory-is-not-empty"), show=True)
971 run_cmd("stat %s" % test_tmpdir, show=True)
972 run_cmd("stat %s" % testbase, show=True)
973 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
980 if options.rebase is not None:
981 rebase_tree(options.rebase, rebase_branch=options.branch)
983 cleanup_list.append(gitroot + "/autobuild.pid")
985 elapsed_time = time.time() - start_time
986 email_failure(-1, 'rebase', 'rebase', 'rebase',
987 'rebase on %s failed' % options.branch,
988 elapsed_time, log_base=options.log_base)
990 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
993 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
994 if status != 0 or errstr != "retry":
1001 cleanup_list.append(gitroot + "/autobuild.pid")
1007 do_print("waiting for tail to flush")
1010 elapsed_time = time.time() - start_time
1012 if options.passcmd is not None:
1013 do_print("Running passcmd: %s" % options.passcmd)
1014 run_cmd(options.passcmd, dir=test_master)
1015 if options.pushto is not None:
1016 push_to(options.pushto, push_branch=options.branch)
1017 if options.keeplogs or options.attach_logs:
1018 blist.tarlogs("logs.tar.gz")
1019 do_print("Logs in logs.tar.gz")
1020 if options.always_email:
1021 email_success(elapsed_time, log_base=options.log_base)
1027 # something failed, gather a tar of the logs
1028 blist.tarlogs("logs.tar.gz")
1030 if options.email is not None:
1031 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1032 elapsed_time, log_base=options.log_base)
1034 elapsed_minutes = elapsed_time / 60.0
1037 ####################################################################
1041 Your autobuild[%s] on %s failed after %.1f minutes
1042 when trying to test %s with the following error:
1046 the autobuild has been abandoned. Please fix the error and resubmit.
1048 ####################################################################
1050 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1054 do_print("Logs in logs.tar.gz")