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_ntvfs "
98 "--exclude-env=ad_dc_no_nss "
99 "--exclude-env=fl2003dc "
100 "--exclude-env=fl2008r2dc "
101 "--exclude-env=ad_member "
102 "--exclude-env=ad_member_idmap_rid "
103 "--exclude-env=ad_member_idmap_ad "
104 "--exclude-env=chgdcpass "
105 "--exclude-env=vampire_2000_dc "
106 "--exclude-env=fl2000dc "
107 "--exclude-env=fileserver "
108 "--exclude-env=backupfromdc "
109 "--exclude-env=restoredc "
110 "--exclude-env=renamedc "
111 "--exclude-env=offlinebackupdc "
112 "--exclude-env=labdc "
115 ("install", "make install", "text/plain"),
116 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
117 ("clean", "make clean", "text/plain")],
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 "
123 "TESTS='--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
124 ("install", "make install", "text/plain"),
125 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
126 ("clean", "make clean", "text/plain")],
128 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
129 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
130 ("make", "make -j", "text/plain"),
131 ("test", "make test FAIL_IMMEDIATELY=1 "
132 "TESTS='--include-env=fileserver'", "text/plain"),
133 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
135 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
136 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
137 ("make", "make -j", "text/plain"),
138 ("test", "make test FAIL_IMMEDIATELY=1 "
139 "TESTS='--include-env=ad_dc "
140 "--include-env=fl2003dc "
141 "--include-env=fl2008r2dc "
142 "--include-env=ad_member "
143 "--include-env=ad_member_idmap_rid "
144 "--include-env=ad_member_idmap_ad'", "text/plain"),
145 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
147 "samba-ad-dc-2": [("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 "
151 "TESTS='--include-env=chgdcpass "
152 "--include-env=vampire_2000_dc "
153 "--include-env=fl2000dc "
154 "--include-env=ad_dc_no_nss "
157 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
159 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
160 # This is currently the longest task, so we don't randomly delay it.
161 "samba-ad-dc-ntvfs": [
162 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
163 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
164 ("make", "make -j", "text/plain"),
165 ("test", "make test FAIL_IMMEDIATELY=1 "
166 "TESTS='--include-env=ad_dc_ntvfs'",
168 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
170 # run the backup/restore testenvs separately as they're fairly standalone
171 # (and CI seems to max out at ~8 different DCs running at once)
172 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
173 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
174 ("make", "make -j", "text/plain"),
175 ("test", "make test FAIL_IMMEDIATELY=1 "
176 "TESTS='--include-env=backupfromdc "
177 "--include-env=restoredc "
178 "--include-env=renamedc "
179 "--include-env=offlinebackupdc "
180 "--include-env=labdc "
183 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
185 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
186 ("make", "make -j", "text/plain"),
187 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
189 # Test cross-compile infrastructure
190 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
191 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
192 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
193 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
194 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
195 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
196 ("compare-results", "script/compare_cc_results.py "
197 "./bin/c4che/default{} "
198 "./bin-xe/c4che/default{} "
199 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
201 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
202 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
203 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
204 ("make", "make -j", "text/plain"),
205 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
206 "TESTS='--include-env=ad_dc'", "text/plain"),
207 ("install", "make install", "text/plain"),
208 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
209 ("clean", "make clean", "text/plain")],
211 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
213 # make sure we have tdb around:
214 ("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"),
215 ("tdb-make", "cd lib/tdb && make", "text/plain"),
216 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
219 # build samba with cluster support (also building ctdb):
220 ("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"),
221 ("samba-make", "make", "text/plain"),
222 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
223 ("samba-install", "make install", "text/plain"),
224 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
227 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
228 ("clean", "make clean", "text/plain"),
229 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
232 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
233 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
234 ("talloc-make", "cd lib/talloc && make", "text/plain"),
235 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
237 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
238 ("tdb-make", "cd lib/tdb && make", "text/plain"),
239 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
241 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
242 ("tevent-make", "cd lib/tevent && make", "text/plain"),
243 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
245 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
246 ("ldb-make", "cd lib/ldb && make", "text/plain"),
247 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
249 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
250 ("nondevel-make", "make -j", "text/plain"),
251 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
252 ("nondevel-install", "make install", "text/plain"),
253 ("nondevel-dist", "make dist", "text/plain"),
255 # retry with all modules shared
256 ("allshared-distclean", "make distclean", "text/plain"),
257 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
258 ("allshared-make", "make -j", "text/plain")],
261 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
262 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
263 ("make", "make -j", "text/plain"),
264 ("test", "make test "
265 "FAIL_IMMEDIATELY=1 "
266 "TESTS='--include-env=none'",
270 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
271 # build with all modules static
272 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
273 ("allstatic-make", "make -j", "text/plain"),
274 ("allstatic-test", "make test "
275 "FAIL_IMMEDIATELY=1 "
276 "TESTS='samba3.smb2.create.*nt4_dc'",
279 # retry without any required modules
280 ("none-distclean", "make distclean", "text/plain"),
281 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
282 ("none-make", "make -j", "text/plain"),
284 # retry with nonshared smbd and smbtorture
285 ("nonshared-distclean", "make distclean", "text/plain"),
286 ("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"),
287 ("nonshared-make", "make -j", "text/plain")],
289 "samba-systemkrb5": [
290 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
291 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
292 ("make", "make -j", "text/plain"),
293 # we currently cannot run a full make test, a limited list of tests could be run
294 # via "make test TESTS=sometests"
295 ("test", "make test FAIL_IMMEDIATELY=1 "
296 "TESTS='--include-env=ktest'", "text/plain"),
297 ("install", "make install", "text/plain"),
298 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
299 ("clean", "make clean", "text/plain")
302 # Test Samba without python still builds. When this test fails
303 # due to more use of Python, the expectations is that the newly
304 # failing part of the code should be disabled when
305 # --disable-python is set (rather than major work being done to
306 # support this environment). The target here is for vendors
307 # shipping a minimal smbd.
309 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
310 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
311 ("make", "make -j", "text/plain"),
312 ("install", "make install", "text/plain"),
313 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
314 ("clean", "make clean", "text/plain"),
316 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
317 ("talloc-make", "cd lib/talloc && make", "text/plain"),
318 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
320 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
321 ("tdb-make", "cd lib/tdb && make", "text/plain"),
322 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
324 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
325 ("tevent-make", "cd lib/tevent && make", "text/plain"),
326 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
328 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
329 ("ldb-make", "cd lib/ldb && make", "text/plain"),
330 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
332 # retry against installed library packages
333 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
334 ("libs-make", "make -j", "text/plain"),
335 ("libs-install", "make install", "text/plain"),
336 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
337 ("libs-clean", "make clean", "text/plain")
340 # check we can do the same thing using python2
341 "samba-nopython-py2": [
342 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
343 ("configure", "PYTHON=python2 ./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
344 ("make", "PYTHON=python2 make -j", "text/plain"),
345 ("install", "PYTHON=python2 make install", "text/plain"),
346 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
347 ("clean", "PYTHON=python2 make clean", "text/plain"),
349 ("talloc-configure", "cd lib/talloc && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
350 ("talloc-make", "cd lib/talloc && PYTHON=python2 make", "text/plain"),
351 ("talloc-install", "cd lib/talloc && PYTHON=python2 make install", "text/plain"),
353 ("tdb-configure", "cd lib/tdb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
354 ("tdb-make", "cd lib/tdb && PYTHON=python2 make", "text/plain"),
355 ("tdb-install", "cd lib/tdb && PYTHON=python2 make install", "text/plain"),
357 ("tevent-configure", "cd lib/tevent && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
358 ("tevent-make", "cd lib/tevent && PYTHON=python2 make", "text/plain"),
359 ("tevent-install", "cd lib/tevent && PYTHON=python2 make install", "text/plain"),
361 ("ldb-configure", "cd lib/ldb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
362 ("ldb-make", "cd lib/ldb && PYTHON=python2 make", "text/plain"),
363 ("ldb-install", "cd lib/ldb && PYTHON=python2 make install", "text/plain"),
365 # retry against installed library packages
366 ("libs-configure", "PYTHON=python2 " + samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
367 ("libs-make", "PYTHON=python2 make -j", "text/plain"),
368 ("libs-install", "PYTHON=python2 make install", "text/plain"),
369 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
370 ("libs-clean", "PYTHON=python2 make clean", "text/plain")
374 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
375 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
376 ("make", "make", "text/plain"),
377 ("install", "make install", "text/plain"),
378 ("test", "make test", "text/plain"),
379 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX}", "text/plain"),
380 ("make-no-lmdb", "make", "text/plain"),
381 ("install-no-lmdb", "make install", "text/plain"),
382 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
383 ("distcheck", "make distcheck", "text/plain"),
384 ("clean", "make clean", "text/plain")],
387 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
388 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
389 ("make", "make", "text/plain"),
390 ("install", "make install", "text/plain"),
391 ("test", "make test", "text/plain"),
392 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
393 ("distcheck", "make distcheck", "text/plain"),
394 ("clean", "make clean", "text/plain")],
397 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
398 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
399 ("make", "make", "text/plain"),
400 ("install", "make install", "text/plain"),
401 ("test", "make test", "text/plain"),
402 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
403 ("distcheck", "make distcheck", "text/plain"),
404 ("clean", "make clean", "text/plain")],
407 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
408 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
409 ("make", "make", "text/plain"),
410 ("install", "make install", "text/plain"),
411 ("test", "make test", "text/plain"),
412 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
413 ("distcheck", "make distcheck", "text/plain"),
414 ("clean", "make clean", "text/plain")],
417 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
418 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
419 ("make", "make", "text/plain"),
420 ("install", "make install", "text/plain"),
421 ("test", "make test", "text/plain"),
422 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
423 ("distcheck", "make distcheck", "text/plain"),
424 ("clean", "make clean", "text/plain")],
427 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
428 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
429 ("touch", "touch *.yp", "text/plain"),
430 ("make", "make", "text/plain"),
431 ("test", "make test", "text/plain"),
432 ("install", "make install", "text/plain"),
433 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
434 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
435 ("clean", "make clean", "text/plain")],
438 # these are useful for debugging autobuild
439 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
440 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
452 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
454 show = options.verbose
456 do_print("Running: '%s' in '%s'" % (cmd, dir))
458 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
460 return check_call(cmd, shell=True, cwd=dir)
462 return call(cmd, shell=True, cwd=dir)
465 class builder(object):
466 '''handle build of one directory'''
468 def __init__(self, name, sequence, cp=True):
470 if name in builddirs:
471 self.dir = builddirs[name]
475 self.tag = self.name.replace('/', '_')
476 self.sequence = sequence
478 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
479 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
481 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
482 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
483 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
484 self.stdout = open(self.stdout_path, 'w')
485 self.stderr = open(self.stderr_path, 'w')
486 self.stdin = open("/dev/null", 'r')
487 self.sdir = "%s/%s" % (testbase, self.tag)
488 self.prefix = "%s/%s" % (test_prefix, self.tag)
489 run_cmd("rm -rf %s" % self.sdir)
490 run_cmd("rm -rf %s" % self.prefix)
492 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.sdir), dir=test_master, show=True)
494 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.sdir), dir=test_master, show=True)
497 def start_next(self):
498 if self.next == len(self.sequence):
499 if not options.nocleanup:
500 run_cmd("rm -rf %s" % self.sdir)
501 run_cmd("rm -rf %s" % self.prefix)
502 do_print('%s: Completed OK' % self.name)
505 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
506 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
507 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
508 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
509 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
510 # if self.output_mime_type == "text/x-subunit":
511 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
513 os.chdir("%s/%s" % (self.sdir, self.dir))
514 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
515 self.proc = Popen(self.cmd, shell=True,
517 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
522 class buildlist(object):
523 '''handle build of multiple directories'''
525 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
528 self.tail_proc = None
531 if options.restrict_tests:
532 tasknames = ["samba-test-only"]
534 tasknames = defaulttasks
536 # If we are only running one test,
537 # do not sleep randomly to wait for it to start
538 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
541 b = builder(n, tasks[n], cp=n is not "pidl")
544 rebase_remote = "rebaseon"
545 retry_task = [("retry",
547 git remote add -t %s %s %s
551 git describe %s/%s > old_remote_branch.desc
553 git describe %s/%s > remote_branch.desc
554 diff old_remote_branch.desc remote_branch.desc
557 rebase_branch, rebase_remote, rebase_url,
559 rebase_remote, rebase_branch,
561 rebase_remote, rebase_branch
565 self.retry = builder('retry', retry_task, cp=False)
566 self.need_retry = False
569 if self.tail_proc is not None:
570 self.tail_proc.terminate()
571 self.tail_proc.wait()
572 self.tail_proc = None
573 if self.retry is not None:
574 self.retry.proc.terminate()
575 self.retry.proc.wait()
578 if b.proc is not None:
579 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.sdir, checkfail=False)
591 b.status = b.proc.poll()
597 ret = self.retry.proc.poll()
599 self.need_retry = True
609 if options.retry and self.need_retry:
611 do_print("retry needed")
612 return (0, None, None, None, "retry")
615 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
617 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
620 return (0, None, None, None, "All OK")
622 def write_system_info(self):
623 filename = 'system-info.txt'
624 f = open(filename, 'w')
625 for cmd in ['uname -a',
632 'df -m %s' % testbase]:
633 out = run_cmd(cmd, output=True, checkfail=False)
634 print('### %s' % cmd, file=f)
635 print(out.decode('utf8', 'backslashreplace'), file=f)
640 def tarlogs(self, fname):
641 tar = tarfile.open(fname, "w:gz")
643 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
644 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
645 if os.path.exists("autobuild.log"):
646 tar.add("autobuild.log")
647 sys_info = self.write_system_info()
651 def remove_logs(self):
653 os.unlink(b.stdout_path)
654 os.unlink(b.stderr_path)
656 def start_tail(self):
659 cmd.append(b.stdout_path)
660 cmd.append(b.stderr_path)
661 self.tail_proc = Popen(cmd, close_fds=True)
665 if options.nocleanup:
667 run_cmd("stat %s || true" % test_tmpdir, show=True)
668 run_cmd("stat %s" % testbase, show=True)
669 do_print("Cleaning up %r" % cleanup_list)
670 for d in cleanup_list:
671 run_cmd("rm -rf %s" % d)
675 '''get to the top of the git repo'''
678 if os.path.isdir(os.path.join(p, ".git")):
680 p = os.path.abspath(os.path.join(p, '..'))
684 def daemonize(logfile):
686 if pid == 0: # Parent
689 if pid != 0: # Actual daemon
694 import resource # Resource usage information.
695 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
696 if maxfd == resource.RLIM_INFINITY:
697 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
698 for fd in range(0, maxfd):
703 os.open(logfile, os.O_RDWR | os.O_CREAT)
708 def write_pidfile(fname):
709 '''write a pid file, cleanup on exit'''
710 f = open(fname, mode='w')
711 f.write("%u\n" % os.getpid())
715 def rebase_tree(rebase_url, rebase_branch="master"):
716 rebase_remote = "rebaseon"
717 do_print("Rebasing on %s" % rebase_url)
718 run_cmd("git describe HEAD", show=True, dir=test_master)
719 run_cmd("git remote add -t %s %s %s" %
720 (rebase_branch, rebase_remote, rebase_url),
721 show=True, dir=test_master)
722 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
723 if options.fix_whitespace:
724 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
725 (rebase_remote, rebase_branch),
726 show=True, dir=test_master)
728 run_cmd("git rebase --force-rebase %s/%s" %
729 (rebase_remote, rebase_branch),
730 show=True, dir=test_master)
731 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
732 (rebase_remote, rebase_branch),
733 dir=test_master, output=True)
735 do_print("No differences between HEAD and %s/%s - exiting" %
736 (rebase_remote, rebase_branch))
738 run_cmd("git describe %s/%s" %
739 (rebase_remote, rebase_branch),
740 show=True, dir=test_master)
741 run_cmd("git describe HEAD", show=True, dir=test_master)
742 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
743 (rebase_remote, rebase_branch),
744 show=True, dir=test_master)
747 def push_to(push_url, push_branch="master"):
748 push_remote = "pushto"
749 do_print("Pushing to %s" % push_url)
751 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
752 run_cmd("git commit --amend -c HEAD", dir=test_master)
753 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
754 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
755 run_cmd("git remote add -t %s %s %s" %
756 (push_branch, push_remote, push_url),
757 show=True, dir=test_master)
758 run_cmd("git push %s +HEAD:%s" %
759 (push_remote, push_branch),
760 show=True, dir=test_master)
763 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
765 gitroot = find_git_root()
767 raise Exception("Failed to find git root")
769 parser = OptionParser()
770 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
771 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
772 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
773 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
774 default=def_testbase)
775 parser.add_option("", "--passcmd", help="command to run on success", default=None)
776 parser.add_option("", "--verbose", help="show all commands as they are run",
777 default=False, action="store_true")
778 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
779 default=None, type='str')
780 parser.add_option("", "--pushto", help="push to a git url on success",
781 default=None, type='str')
782 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
783 default=False, action="store_true")
784 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
785 default=False, action="store_true")
786 parser.add_option("", "--retry", help="automatically retry if master changes",
787 default=False, action="store_true")
788 parser.add_option("", "--email", help="send email to the given address on failure",
789 type='str', default=None)
790 parser.add_option("", "--email-from", help="send email from the given address",
791 type='str', default="autobuild@samba.org")
792 parser.add_option("", "--email-server", help="send email via the given server",
793 type='str', default='localhost')
794 parser.add_option("", "--always-email", help="always send email, even on success",
796 parser.add_option("", "--daemon", help="daemonize after initial setup",
798 parser.add_option("", "--branch", help="the branch to work on (default=master)",
799 default="master", type='str')
800 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
801 default=gitroot, type='str')
802 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
803 default=False, action="store_true")
804 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
808 def send_email(subject, text, log_tar):
809 if options.email is None:
810 do_print("not sending email because the recipient is not set")
811 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
814 outer = MIMEMultipart()
815 outer['Subject'] = subject
816 outer['To'] = options.email
817 outer['From'] = options.email_from
818 outer['Date'] = email.utils.formatdate(localtime=True)
819 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
820 outer.attach(MIMEText(text, 'plain'))
821 if options.attach_logs:
822 fp = open(log_tar, 'rb')
823 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
825 # Set the filename parameter
826 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
828 content = outer.as_string()
829 s = smtplib.SMTP(options.email_server)
830 s.sendmail(options.email_from, [options.email], content)
835 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
836 elapsed_time, log_base=None, add_log_tail=True):
837 '''send an email to options.email about the failure'''
838 elapsed_minutes = elapsed_time / 60.0
844 Your autobuild on %s failed after %.1f minutes
845 when trying to test %s with the following error:
849 the autobuild has been abandoned. Please fix the error and resubmit.
851 A summary of the autobuild process is here:
854 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
856 if options.restrict_tests:
858 The build was restricted to tests matching %s\n""" % options.restrict_tests
860 if failed_task != 'rebase':
862 You can see logs of the failed task here:
867 or you can get full logs of all tasks in this job here:
871 The top commit for the tree that was built was:
875 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
878 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
879 lines = f.readlines()
880 log_tail = "".join(lines[-50:])
881 num_lines = len(lines)
883 # Also include stderr (compile failures) if < 50 lines of stdout
884 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
885 log_tail += "".join(f.readlines()[-(50 - num_lines):])
888 The last 50 lines of log messages:
894 logs = os.path.join(gitroot, 'logs.tar.gz')
895 send_email('autobuild[%s] failure on %s for task %s during %s'
896 % (options.branch, platform.node(), failed_task, failed_stage),
900 def email_success(elapsed_time, log_base=None):
901 '''send an email to options.email about a successful build'''
907 Your autobuild on %s has succeeded after %.1f minutes.
909 ''' % (platform.node(), elapsed_time / 60.)
911 if options.restrict_tests:
913 The build was restricted to tests matching %s\n""" % options.restrict_tests
918 you can get full logs of all tasks in this job here:
925 The top commit for the tree that was built was:
930 logs = os.path.join(gitroot, 'logs.tar.gz')
931 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
935 (options, args) = parser.parse_args()
938 if options.rebase is None:
939 raise Exception('You can only use --retry if you also rebase')
941 testbase = "%s/b%u" % (options.testbase, os.getpid())
942 test_master = "%s/master" % testbase
943 test_prefix = "%s/prefix" % testbase
944 test_tmpdir = "%s/tmp" % testbase
945 os.environ['TMPDIR'] = test_tmpdir
947 # get the top commit message, for emails
948 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
949 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
952 os.makedirs(testbase)
953 except Exception as reason:
954 raise Exception("Unable to create %s : %s" % (testbase, reason))
955 cleanup_list.append(testbase)
958 logfile = os.path.join(testbase, "log")
959 do_print("Forking into the background, writing progress to %s" % logfile)
962 write_pidfile(gitroot + "/autobuild.pid")
964 start_time = time.time()
968 run_cmd("rm -rf %s" % test_tmpdir, show=True)
969 os.makedirs(test_tmpdir)
970 # The waf uninstall code removes empty directories all the way
971 # up the tree. Creating a file in test_tmpdir stops it from
973 run_cmd("touch %s" % os.path.join(test_tmpdir,
974 ".directory-is-not-empty"), show=True)
975 run_cmd("stat %s" % test_tmpdir, show=True)
976 run_cmd("stat %s" % testbase, show=True)
977 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
984 if options.rebase is not None:
985 rebase_tree(options.rebase, rebase_branch=options.branch)
987 cleanup_list.append(gitroot + "/autobuild.pid")
989 elapsed_time = time.time() - start_time
990 email_failure(-1, 'rebase', 'rebase', 'rebase',
991 'rebase on %s failed' % options.branch,
992 elapsed_time, log_base=options.log_base)
994 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
997 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
998 if status != 0 or errstr != "retry":
1005 cleanup_list.append(gitroot + "/autobuild.pid")
1011 do_print("waiting for tail to flush")
1014 elapsed_time = time.time() - start_time
1016 if options.passcmd is not None:
1017 do_print("Running passcmd: %s" % options.passcmd)
1018 run_cmd(options.passcmd, dir=test_master)
1019 if options.pushto is not None:
1020 push_to(options.pushto, push_branch=options.branch)
1021 if options.keeplogs or options.attach_logs:
1022 blist.tarlogs("logs.tar.gz")
1023 do_print("Logs in logs.tar.gz")
1024 if options.always_email:
1025 email_success(elapsed_time, log_base=options.log_base)
1031 # something failed, gather a tar of the logs
1032 blist.tarlogs("logs.tar.gz")
1034 if options.email is not None:
1035 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1036 elapsed_time, log_base=options.log_base)
1038 elapsed_minutes = elapsed_time / 60.0
1041 ####################################################################
1045 Your autobuild[%s] on %s failed after %.1f minutes
1046 when trying to test %s with the following error:
1050 the autobuild has been abandoned. Please fix the error and resubmit.
1052 ####################################################################
1054 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1058 do_print("Logs in logs.tar.gz")