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'
40 "samba-fileserver": ".",
46 "samba-none-env": ".",
48 "samba-ad-dc-ntvfs": ".",
50 "samba-ad-dc-backup": ".",
51 "samba-systemkrb5": ".",
52 "samba-nopython": ".",
53 "samba-nopython-py2": ".",
56 "talloc": "lib/talloc",
57 "replace": "lib/replace",
58 "tevent": "lib/tevent",
62 defaulttasks = builddirs.keys()
64 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
65 defaulttasks.remove("samba-o3")
67 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
68 samba_configure_params = " --picky-developer ${PREFIX} --with-profiling-data"
70 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
71 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
72 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
73 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
74 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE"
75 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
76 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs
79 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
80 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
81 ("make", "make all", "text/plain"),
82 ("install", "make install", "text/plain"),
83 ("test", "make autotest", "text/plain"),
84 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
85 ("clean", "make clean", "text/plain")],
87 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
89 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
90 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
91 ("make", "make -j", "text/plain"),
92 ("test", "make test FAIL_IMMEDIATELY=1 "
93 "TESTS='--exclude-env=none "
94 "--exclude-env=nt4_dc "
95 "--exclude-env=nt4_member "
96 "--exclude-env=ad_dc "
97 "--exclude-env=ad_dc_backup "
98 "--exclude-env=ad_dc_ntvfs "
99 "--exclude-env=ad_dc_default "
100 "--exclude-env=ad_dc_slowtests "
101 "--exclude-env=ad_dc_no_nss "
102 "--exclude-env=fl2003dc "
103 "--exclude-env=fl2008dc "
104 "--exclude-env=fl2008r2dc "
105 "--exclude-env=ad_member "
106 "--exclude-env=ad_member_idmap_rid "
107 "--exclude-env=ad_member_idmap_ad "
108 "--exclude-env=chgdcpass "
109 "--exclude-env=vampire_2000_dc "
110 "--exclude-env=fl2000dc "
111 "--exclude-env=fileserver "
112 "--exclude-env=backupfromdc "
113 "--exclude-env=restoredc "
114 "--exclude-env=renamedc "
115 "--exclude-env=offlinebackupdc "
116 "--exclude-env=labdc "
119 ("install", "make install", "text/plain"),
120 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
121 ("clean", "make clean", "text/plain")],
123 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
124 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
125 ("make", "make -j", "text/plain"),
126 ("test", "make test FAIL_IMMEDIATELY=1 "
127 "TESTS='--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
128 ("install", "make install", "text/plain"),
129 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
130 ("clean", "make clean", "text/plain")],
132 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
133 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
134 ("make", "make -j", "text/plain"),
135 ("test", "make test FAIL_IMMEDIATELY=1 "
136 "TESTS='--include-env=fileserver'", "text/plain"),
137 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
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 "
143 "TESTS='--include-env=ad_dc "
144 "--include-env=ad_dc_backup "
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 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
153 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
154 ("make", "make -j", "text/plain"),
155 ("test", "make test FAIL_IMMEDIATELY=1 "
156 "TESTS='--include-env=chgdcpass "
157 "--include-env=vampire_2000_dc "
158 "--include-env=fl2000dc "
159 "--include-env=ad_dc_no_nss "
162 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
164 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
165 # This is currently the longest task, so we don't randomly delay it.
166 "samba-ad-dc-ntvfs": [
167 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
168 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
169 ("make", "make -j", "text/plain"),
170 ("test", "make test FAIL_IMMEDIATELY=1 "
172 "--include-env=ad_dc_ntvfs "
173 "--include-env=fl2008dc "
174 "--include-env=ad_dc_default "
175 "--include-env=ad_dc_slowtests "
177 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
179 # run the backup/restore testenvs separately as they're fairly standalone
180 # (and CI seems to max out at ~8 different DCs running at once)
181 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
182 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
183 ("make", "make -j", "text/plain"),
184 ("test", "make test FAIL_IMMEDIATELY=1 "
185 "TESTS='--include-env=backupfromdc "
186 "--include-env=restoredc "
187 "--include-env=renamedc "
188 "--include-env=offlinebackupdc "
189 "--include-env=labdc "
192 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
194 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
195 ("make", "make -j", "text/plain"),
196 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
198 # Test cross-compile infrastructure
199 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
200 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
201 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
202 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
203 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
204 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
205 ("compare-results", "script/compare_cc_results.py "
206 "./bin/c4che/default{} "
207 "./bin-xe/c4che/default{} "
208 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
210 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
211 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
212 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
213 ("make", "make -j", "text/plain"),
214 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
215 "TESTS='--include-env=ad_dc'", "text/plain"),
216 ("install", "make install", "text/plain"),
217 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
218 ("clean", "make clean", "text/plain")],
220 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
222 # make sure we have tdb around:
223 ("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"),
224 ("tdb-make", "cd lib/tdb && make", "text/plain"),
225 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
228 # build samba with cluster support (also building ctdb):
229 ("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"),
230 ("samba-make", "make", "text/plain"),
231 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
232 ("samba-install", "make install", "text/plain"),
233 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
236 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
237 ("clean", "make clean", "text/plain"),
238 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
241 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
242 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
243 ("talloc-make", "cd lib/talloc && make", "text/plain"),
244 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
246 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
247 ("tdb-make", "cd lib/tdb && make", "text/plain"),
248 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
250 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
251 ("tevent-make", "cd lib/tevent && make", "text/plain"),
252 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
254 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
255 ("ldb-make", "cd lib/ldb && make", "text/plain"),
256 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
258 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
259 ("nondevel-make", "make -j", "text/plain"),
260 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
261 ("nondevel-install", "make install", "text/plain"),
262 ("nondevel-dist", "make dist", "text/plain"),
264 # retry with all modules shared
265 ("allshared-distclean", "make distclean", "text/plain"),
266 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
267 ("allshared-make", "make -j", "text/plain")],
270 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
271 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
272 ("make", "make -j", "text/plain"),
273 ("test", "make test "
274 "FAIL_IMMEDIATELY=1 "
275 "TESTS='--include-env=none'",
279 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
280 # build with all modules static
281 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
282 ("allstatic-make", "make -j", "text/plain"),
283 ("allstatic-test", "make test "
284 "FAIL_IMMEDIATELY=1 "
285 "TESTS='samba3.smb2.create.*nt4_dc'",
288 # retry without any required modules
289 ("none-distclean", "make distclean", "text/plain"),
290 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
291 ("none-make", "make -j", "text/plain"),
293 # retry with nonshared smbd and smbtorture
294 ("nonshared-distclean", "make distclean", "text/plain"),
295 ("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"),
296 ("nonshared-make", "make -j", "text/plain")],
298 "samba-systemkrb5": [
299 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
300 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
301 ("make", "make -j", "text/plain"),
302 # we currently cannot run a full make test, a limited list of tests could be run
303 # via "make test TESTS=sometests"
304 ("test", "make test FAIL_IMMEDIATELY=1 "
305 "TESTS='--include-env=ktest'", "text/plain"),
306 ("install", "make install", "text/plain"),
307 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
308 ("clean", "make clean", "text/plain")
311 # Test Samba without python still builds. When this test fails
312 # due to more use of Python, the expectations is that the newly
313 # failing part of the code should be disabled when
314 # --disable-python is set (rather than major work being done to
315 # support this environment). The target here is for vendors
316 # shipping a minimal smbd.
318 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
319 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
320 ("make", "make -j", "text/plain"),
321 ("install", "make install", "text/plain"),
322 ("test", "make test-nopython", "text/plain"),
323 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
324 ("clean", "make clean", "text/plain"),
326 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
327 ("talloc-make", "cd lib/talloc && make", "text/plain"),
328 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
330 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
331 ("tdb-make", "cd lib/tdb && make", "text/plain"),
332 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
334 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
335 ("tevent-make", "cd lib/tevent && make", "text/plain"),
336 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
338 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
339 ("ldb-make", "cd lib/ldb && make", "text/plain"),
340 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
342 # retry against installed library packages
343 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
344 ("libs-make", "make -j", "text/plain"),
345 ("libs-install", "make install", "text/plain"),
346 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
347 ("libs-clean", "make clean", "text/plain")
350 # check we can do the same thing using python2
351 "samba-nopython-py2": [
352 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
353 ("configure", "PYTHON=python2 ./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
354 ("make", "PYTHON=python2 make -j", "text/plain"),
355 ("install", "PYTHON=python2 make install", "text/plain"),
356 ("test", "make test-nopython", "text/plain"),
357 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
358 ("clean", "PYTHON=python2 make clean", "text/plain"),
360 ("talloc-configure", "cd lib/talloc && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
361 ("talloc-make", "cd lib/talloc && PYTHON=python2 make", "text/plain"),
362 ("talloc-install", "cd lib/talloc && PYTHON=python2 make install", "text/plain"),
364 ("tdb-configure", "cd lib/tdb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
365 ("tdb-make", "cd lib/tdb && PYTHON=python2 make", "text/plain"),
366 ("tdb-install", "cd lib/tdb && PYTHON=python2 make install", "text/plain"),
368 ("tevent-configure", "cd lib/tevent && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
369 ("tevent-make", "cd lib/tevent && PYTHON=python2 make", "text/plain"),
370 ("tevent-install", "cd lib/tevent && PYTHON=python2 make install", "text/plain"),
372 ("ldb-configure", "cd lib/ldb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
373 ("ldb-make", "cd lib/ldb && PYTHON=python2 make", "text/plain"),
374 ("ldb-install", "cd lib/ldb && PYTHON=python2 make install", "text/plain"),
376 # retry against installed library packages
377 ("libs-configure", "PYTHON=python2 " + samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
378 ("libs-make", "PYTHON=python2 make -j", "text/plain"),
379 ("libs-install", "PYTHON=python2 make install", "text/plain"),
380 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
381 ("libs-clean", "PYTHON=python2 make clean", "text/plain")
385 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
386 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
387 ("make", "make", "text/plain"),
388 ("install", "make install", "text/plain"),
389 ("test", "make test", "text/plain"),
390 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX}", "text/plain"),
391 ("make-no-lmdb", "make", "text/plain"),
392 ("install-no-lmdb", "make install", "text/plain"),
393 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
394 ("distcheck", "make distcheck", "text/plain"),
395 ("clean", "make clean", "text/plain")],
398 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
399 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
400 ("make", "make", "text/plain"),
401 ("install", "make install", "text/plain"),
402 ("test", "make test", "text/plain"),
403 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
404 ("distcheck", "make distcheck", "text/plain"),
405 ("clean", "make clean", "text/plain")],
408 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
409 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
410 ("make", "make", "text/plain"),
411 ("install", "make install", "text/plain"),
412 ("test", "make test", "text/plain"),
413 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
414 ("distcheck", "make distcheck", "text/plain"),
415 ("clean", "make clean", "text/plain")],
418 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
419 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
420 ("make", "make", "text/plain"),
421 ("install", "make install", "text/plain"),
422 ("test", "make test", "text/plain"),
423 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
424 ("distcheck", "make distcheck", "text/plain"),
425 ("clean", "make clean", "text/plain")],
428 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
429 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
430 ("make", "make", "text/plain"),
431 ("install", "make install", "text/plain"),
432 ("test", "make test", "text/plain"),
433 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
434 ("distcheck", "make distcheck", "text/plain"),
435 ("clean", "make clean", "text/plain")],
438 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
439 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
440 ("touch", "touch *.yp", "text/plain"),
441 ("make", "make", "text/plain"),
442 ("test", "make test", "text/plain"),
443 ("install", "make install", "text/plain"),
444 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
445 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
446 ("clean", "make clean", "text/plain")],
449 # these are useful for debugging autobuild
450 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
451 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
463 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
465 show = options.verbose
467 do_print("Running: '%s' in '%s'" % (cmd, dir))
469 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
471 return check_call(cmd, shell=True, cwd=dir)
473 return call(cmd, shell=True, cwd=dir)
476 class builder(object):
477 '''handle build of one directory'''
479 def __init__(self, name, sequence, cp=True):
481 if name in builddirs:
482 self.dir = builddirs[name]
486 self.tag = self.name.replace('/', '_')
487 self.sequence = sequence
489 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
490 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
492 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
493 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
494 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
495 self.stdout = open(self.stdout_path, 'w')
496 self.stderr = open(self.stderr_path, 'w')
497 self.stdin = open("/dev/null", 'r')
498 self.sdir = "%s/%s" % (testbase, self.tag)
499 self.prefix = "%s/%s" % (test_prefix, self.tag)
500 run_cmd("rm -rf %s" % self.sdir)
501 run_cmd("rm -rf %s" % self.prefix)
503 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
505 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
508 def start_next(self):
509 if self.next == len(self.sequence):
510 if not options.nocleanup:
511 run_cmd("rm -rf %s" % self.sdir)
512 run_cmd("rm -rf %s" % self.prefix)
513 do_print('%s: Completed OK' % self.name)
516 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
517 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
518 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
519 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
520 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
521 # if self.output_mime_type == "text/x-subunit":
522 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
524 os.chdir("%s/%s" % (self.sdir, self.dir))
525 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
526 self.proc = Popen(self.cmd, shell=True,
528 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
533 class buildlist(object):
534 '''handle build of multiple directories'''
536 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
539 self.tail_proc = None
542 if options.restrict_tests:
543 tasknames = ["samba-test-only"]
545 tasknames = defaulttasks
547 # If we are only running one test,
548 # do not sleep randomly to wait for it to start
549 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
552 b = builder(n, tasks[n], cp=n is not "pidl")
555 rebase_remote = "rebaseon"
556 retry_task = [("retry",
558 git remote add -t %s %s %s
562 git describe %s/%s > old_remote_branch.desc
564 git describe %s/%s > remote_branch.desc
565 diff old_remote_branch.desc remote_branch.desc
568 rebase_branch, rebase_remote, rebase_url,
570 rebase_remote, rebase_branch,
572 rebase_remote, rebase_branch
576 self.retry = builder('retry', retry_task, cp=False)
577 self.need_retry = False
580 if self.tail_proc is not None:
581 self.tail_proc.terminate()
582 self.tail_proc.wait()
583 self.tail_proc = None
584 if self.retry is not None:
585 self.retry.proc.terminate()
586 self.retry.proc.wait()
589 if b.proc is not None:
590 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
602 b.status = b.proc.poll()
608 ret = self.retry.proc.poll()
610 self.need_retry = True
620 if options.retry and self.need_retry:
622 do_print("retry needed")
623 return (0, None, None, None, "retry")
626 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
628 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
631 return (0, None, None, None, "All OK")
633 def write_system_info(self):
634 filename = 'system-info.txt'
635 f = open(filename, 'w')
636 for cmd in ['uname -a',
643 'df -m %s' % testbase]:
644 out = run_cmd(cmd, output=True, checkfail=False)
645 print('### %s' % cmd, file=f)
646 print(out.decode('utf8', 'backslashreplace'), file=f)
651 def tarlogs(self, fname):
652 tar = tarfile.open(fname, "w:gz")
654 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
655 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
656 if os.path.exists("autobuild.log"):
657 tar.add("autobuild.log")
658 sys_info = self.write_system_info()
662 def remove_logs(self):
664 os.unlink(b.stdout_path)
665 os.unlink(b.stderr_path)
667 def start_tail(self):
670 cmd.append(b.stdout_path)
671 cmd.append(b.stderr_path)
672 self.tail_proc = Popen(cmd, close_fds=True)
676 if options.nocleanup:
678 run_cmd("stat %s || true" % test_tmpdir, show=True)
679 run_cmd("stat %s" % testbase, show=True)
680 do_print("Cleaning up %r" % cleanup_list)
681 for d in cleanup_list:
682 run_cmd("rm -rf %s" % d)
686 '''get to the top of the git repo'''
689 if os.path.isdir(os.path.join(p, ".git")):
691 p = os.path.abspath(os.path.join(p, '..'))
695 def daemonize(logfile):
697 if pid == 0: # Parent
700 if pid != 0: # Actual daemon
705 import resource # Resource usage information.
706 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
707 if maxfd == resource.RLIM_INFINITY:
708 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
709 for fd in range(0, maxfd):
714 os.open(logfile, os.O_RDWR | os.O_CREAT)
719 def write_pidfile(fname):
720 '''write a pid file, cleanup on exit'''
721 f = open(fname, mode='w')
722 f.write("%u\n" % os.getpid())
726 def rebase_tree(rebase_url, rebase_branch="master"):
727 rebase_remote = "rebaseon"
728 do_print("Rebasing on %s" % rebase_url)
729 run_cmd("git describe HEAD", show=True, dir=test_master)
730 run_cmd("git remote add -t %s %s %s" %
731 (rebase_branch, rebase_remote, rebase_url),
732 show=True, dir=test_master)
733 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
734 if options.fix_whitespace:
735 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
736 (rebase_remote, rebase_branch),
737 show=True, dir=test_master)
739 run_cmd("git rebase --force-rebase %s/%s" %
740 (rebase_remote, rebase_branch),
741 show=True, dir=test_master)
742 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
743 (rebase_remote, rebase_branch),
744 dir=test_master, output=True)
746 do_print("No differences between HEAD and %s/%s - exiting" %
747 (rebase_remote, rebase_branch))
749 run_cmd("git describe %s/%s" %
750 (rebase_remote, rebase_branch),
751 show=True, dir=test_master)
752 run_cmd("git describe HEAD", show=True, dir=test_master)
753 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
754 (rebase_remote, rebase_branch),
755 show=True, dir=test_master)
758 def push_to(push_url, push_branch="master"):
759 push_remote = "pushto"
760 do_print("Pushing to %s" % push_url)
762 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
763 run_cmd("git commit --amend -c HEAD", dir=test_master)
764 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
765 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
766 run_cmd("git remote add -t %s %s %s" %
767 (push_branch, push_remote, push_url),
768 show=True, dir=test_master)
769 run_cmd("git push %s +HEAD:%s" %
770 (push_remote, push_branch),
771 show=True, dir=test_master)
774 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
776 gitroot = find_git_root()
778 raise Exception("Failed to find git root")
780 parser = OptionParser()
781 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
782 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
783 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
784 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
785 default=def_testbase)
786 parser.add_option("", "--passcmd", help="command to run on success", default=None)
787 parser.add_option("", "--verbose", help="show all commands as they are run",
788 default=False, action="store_true")
789 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
790 default=None, type='str')
791 parser.add_option("", "--pushto", help="push to a git url on success",
792 default=None, type='str')
793 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
794 default=False, action="store_true")
795 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
796 default=False, action="store_true")
797 parser.add_option("", "--retry", help="automatically retry if master changes",
798 default=False, action="store_true")
799 parser.add_option("", "--email", help="send email to the given address on failure",
800 type='str', default=None)
801 parser.add_option("", "--email-from", help="send email from the given address",
802 type='str', default="autobuild@samba.org")
803 parser.add_option("", "--email-server", help="send email via the given server",
804 type='str', default='localhost')
805 parser.add_option("", "--always-email", help="always send email, even on success",
807 parser.add_option("", "--daemon", help="daemonize after initial setup",
809 parser.add_option("", "--branch", help="the branch to work on (default=master)",
810 default="master", type='str')
811 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
812 default=gitroot, type='str')
813 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
814 default=False, action="store_true")
815 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
819 def send_email(subject, text, log_tar):
820 if options.email is None:
821 do_print("not sending email because the recipient is not set")
822 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
825 outer = MIMEMultipart()
826 outer['Subject'] = subject
827 outer['To'] = options.email
828 outer['From'] = options.email_from
829 outer['Date'] = email.utils.formatdate(localtime=True)
830 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
831 outer.attach(MIMEText(text, 'plain'))
832 if options.attach_logs:
833 fp = open(log_tar, 'rb')
834 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
836 # Set the filename parameter
837 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
839 content = outer.as_string()
840 s = smtplib.SMTP(options.email_server)
841 s.sendmail(options.email_from, [options.email], content)
846 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
847 elapsed_time, log_base=None, add_log_tail=True):
848 '''send an email to options.email about the failure'''
849 elapsed_minutes = elapsed_time / 60.0
855 Your autobuild on %s failed after %.1f minutes
856 when trying to test %s with the following error:
860 the autobuild has been abandoned. Please fix the error and resubmit.
862 A summary of the autobuild process is here:
865 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
867 if options.restrict_tests:
869 The build was restricted to tests matching %s\n""" % options.restrict_tests
871 if failed_task != 'rebase':
873 You can see logs of the failed task here:
878 or you can get full logs of all tasks in this job here:
882 The top commit for the tree that was built was:
886 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
889 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
890 lines = f.readlines()
891 log_tail = "".join(lines[-50:])
892 num_lines = len(lines)
894 # Also include stderr (compile failures) if < 50 lines of stdout
895 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
896 log_tail += "".join(f.readlines()[-(50 - num_lines):])
899 The last 50 lines of log messages:
905 logs = os.path.join(gitroot, 'logs.tar.gz')
906 send_email('autobuild[%s] failure on %s for task %s during %s'
907 % (options.branch, platform.node(), failed_task, failed_stage),
911 def email_success(elapsed_time, log_base=None):
912 '''send an email to options.email about a successful build'''
918 Your autobuild on %s has succeeded after %.1f minutes.
920 ''' % (platform.node(), elapsed_time / 60.)
922 if options.restrict_tests:
924 The build was restricted to tests matching %s\n""" % options.restrict_tests
929 you can get full logs of all tasks in this job here:
936 The top commit for the tree that was built was:
941 logs = os.path.join(gitroot, 'logs.tar.gz')
942 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
946 (options, args) = parser.parse_args()
949 if options.rebase is None:
950 raise Exception('You can only use --retry if you also rebase')
952 testbase = "%s/b%u" % (options.testbase, os.getpid())
953 test_master = "%s/master" % testbase
954 test_prefix = "%s/prefix" % testbase
955 test_tmpdir = "%s/tmp" % testbase
956 os.environ['TMPDIR'] = test_tmpdir
958 # get the top commit message, for emails
959 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
960 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
963 os.makedirs(testbase)
964 except Exception as reason:
965 raise Exception("Unable to create %s : %s" % (testbase, reason))
966 cleanup_list.append(testbase)
969 logfile = os.path.join(testbase, "log")
970 do_print("Forking into the background, writing progress to %s" % logfile)
973 write_pidfile(gitroot + "/autobuild.pid")
975 start_time = time.time()
979 run_cmd("rm -rf %s" % test_tmpdir, show=True)
980 os.makedirs(test_tmpdir)
981 # The waf uninstall code removes empty directories all the way
982 # up the tree. Creating a file in test_tmpdir stops it from
984 run_cmd("touch %s" % os.path.join(test_tmpdir,
985 ".directory-is-not-empty"), show=True)
986 run_cmd("stat %s" % test_tmpdir, show=True)
987 run_cmd("stat %s" % testbase, show=True)
988 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
995 if options.rebase is not None:
996 rebase_tree(options.rebase, rebase_branch=options.branch)
998 cleanup_list.append(gitroot + "/autobuild.pid")
1000 elapsed_time = time.time() - start_time
1001 email_failure(-1, 'rebase', 'rebase', 'rebase',
1002 'rebase on %s failed' % options.branch,
1003 elapsed_time, log_base=options.log_base)
1005 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
1008 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
1009 if status != 0 or errstr != "retry":
1016 cleanup_list.append(gitroot + "/autobuild.pid")
1022 do_print("waiting for tail to flush")
1025 elapsed_time = time.time() - start_time
1027 if options.passcmd is not None:
1028 do_print("Running passcmd: %s" % options.passcmd)
1029 run_cmd(options.passcmd, dir=test_master)
1030 if options.pushto is not None:
1031 push_to(options.pushto, push_branch=options.branch)
1032 if options.keeplogs or options.attach_logs:
1033 blist.tarlogs("logs.tar.gz")
1034 do_print("Logs in logs.tar.gz")
1035 if options.always_email:
1036 email_success(elapsed_time, log_base=options.log_base)
1042 # something failed, gather a tar of the logs
1043 blist.tarlogs("logs.tar.gz")
1045 if options.email is not None:
1046 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1047 elapsed_time, log_base=options.log_base)
1049 elapsed_minutes = elapsed_time / 60.0
1052 ####################################################################
1056 Your autobuild[%s] on %s failed after %.1f minutes
1057 when trying to test %s with the following error:
1061 the autobuild has been abandoned. Please fix the error and resubmit.
1063 ####################################################################
1065 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1069 do_print("Logs in logs.tar.gz")