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'
36 '''get to the top of the git repo'''
39 if os.path.isdir(os.path.join(p, ".git")):
41 p = os.path.abspath(os.path.join(p, '..'))
45 gitroot = find_git_root()
47 raise Exception("Failed to find git root")
50 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
52 parser = OptionParser()
53 parser.add_option("", "--tail", help="show output while running", default=False, action="store_true")
54 parser.add_option("", "--keeplogs", help="keep logs", default=False, action="store_true")
55 parser.add_option("", "--nocleanup", help="don't remove test tree", default=False, action="store_true")
56 parser.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase,
58 parser.add_option("", "--passcmd", help="command to run on success", default=None)
59 parser.add_option("", "--verbose", help="show all commands as they are run",
60 default=False, action="store_true")
61 parser.add_option("", "--rebase", help="rebase on the given tree before testing",
62 default=None, type='str')
63 parser.add_option("", "--pushto", help="push to a git url on success",
64 default=None, type='str')
65 parser.add_option("", "--mark", help="add a Tested-By signoff before pushing",
66 default=False, action="store_true")
67 parser.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
68 default=False, action="store_true")
69 parser.add_option("", "--retry", help="automatically retry if master changes",
70 default=False, action="store_true")
71 parser.add_option("", "--email", help="send email to the given address on failure",
72 type='str', default=None)
73 parser.add_option("", "--email-from", help="send email from the given address",
74 type='str', default="autobuild@samba.org")
75 parser.add_option("", "--email-server", help="send email via the given server",
76 type='str', default='localhost')
77 parser.add_option("", "--always-email", help="always send email, even on success",
79 parser.add_option("", "--daemon", help="daemonize after initial setup",
81 parser.add_option("", "--branch", help="the branch to work on (default=master)",
82 default="master", type='str')
83 parser.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
84 default=gitroot, type='str')
85 parser.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
86 default=False, action="store_true")
87 parser.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
89 parser.add_option("--enable-coverage", dest='enable_coverage',
90 action="store_const", const='--enable-coverage', default='',
91 help="Add --enable-coverage option while configure")
93 (options, args) = parser.parse_args()
96 if options.rebase is None:
97 raise Exception('You can only use --retry if you also rebase')
99 testbase = "%s/b%u" % (options.testbase, os.getpid())
100 test_master = "%s/master" % testbase
101 test_prefix = "%s/prefix" % testbase
102 test_tmpdir = "%s/tmp" % testbase
103 os.environ['TMPDIR'] = test_tmpdir
112 "samba-fileserver": ".",
113 "samba-ad-member": ".",
119 "samba-none-env": ".",
120 "samba-ad-dc-1": ".",
121 "samba-ad-dc-2": ".",
122 "samba-ad-dc-3": ".",
123 "samba-ad-dc-4": ".",
124 "samba-ad-dc-5": ".",
125 "samba-ad-dc-6": ".",
126 "samba-ad-dc-ntvfs": ".",
127 "samba-ad-dc-backup": ".",
128 "samba-systemkrb5": ".",
129 "samba-nopython": ".",
130 "samba-nopython-py2": ".",
131 "samba-schemaupgrade": ".",
134 "talloc": "lib/talloc",
135 "replace": "lib/replace",
136 "tevent": "lib/tevent",
140 defaulttasks = builddirs.keys()
142 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
143 defaulttasks.remove("samba-o3")
145 ctdb_configure_params = " --enable-developer --picky-developer ${PREFIX}"
146 samba_configure_params = " --picky-developer ${PREFIX} --with-profiling-data"
148 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
149 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
150 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
151 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
152 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE"
153 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
154 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs
157 "ctdb": [("random-sleep", "../script/random-sleep.sh 300 900", "text/plain"),
158 ("configure", "./configure " + ctdb_configure_params, "text/plain"),
159 ("make", "make all", "text/plain"),
160 ("install", "make install", "text/plain"),
161 ("test", "make autotest", "text/plain"),
162 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
163 ("clean", "make clean", "text/plain")],
165 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
167 ("random-sleep", "script/random-sleep.sh 300 900", "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 "
171 "TESTS='--exclude-env=none "
172 "--exclude-env=nt4_dc "
173 "--exclude-env=nt4_dc_schannel "
174 "--exclude-env=nt4_member "
175 "--exclude-env=ad_dc "
176 "--exclude-env=ad_dc_backup "
177 "--exclude-env=ad_dc_ntvfs "
178 "--exclude-env=ad_dc_default "
179 "--exclude-env=ad_dc_slowtests "
180 "--exclude-env=ad_dc_no_nss "
181 "--exclude-env=ad_dc_no_ntlm "
182 "--exclude-env=fl2003dc "
183 "--exclude-env=fl2008dc "
184 "--exclude-env=fl2008r2dc "
185 "--exclude-env=ad_member "
186 "--exclude-env=ad_member_idmap_rid "
187 "--exclude-env=ad_member_idmap_ad "
188 "--exclude-env=ad_member_rfc2307 "
189 "--exclude-env=chgdcpass "
190 "--exclude-env=vampire_2000_dc "
191 "--exclude-env=fl2000dc "
192 "--exclude-env=fileserver "
193 "--exclude-env=maptoguest "
194 "--exclude-env=simpleserver "
195 "--exclude-env=backupfromdc "
196 "--exclude-env=restoredc "
197 "--exclude-env=renamedc "
198 "--exclude-env=offlinebackupdc "
199 "--exclude-env=labdc "
200 "--exclude-env=preforkrestartdc "
201 "--exclude-env=proclimitdc "
202 "--exclude-env=promoted_dc "
203 "--exclude-env=vampire_dc "
204 "--exclude-env=rodc "
205 "--exclude-env=ad_dc_default "
206 "--exclude-env=ad_dc_slowtests "
207 "--exclude-env=schema_pair_dc "
208 "--exclude-env=schema_dc "
211 ("install", "make install", "text/plain"),
212 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
213 ("clean", "make clean", "text/plain")],
215 "samba-nt4": [("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
216 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
217 ("make", "make -j", "text/plain"),
218 ("test", "make test FAIL_IMMEDIATELY=1 "
220 "--include-env=nt4_dc "
221 "--include-env=nt4_dc_schannel "
222 "--include-env=nt4_member "
224 ("install", "make install", "text/plain"),
225 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
226 ("clean", "make clean", "text/plain")],
228 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
229 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
230 ("make", "make -j", "text/plain"),
231 ("test", "make test FAIL_IMMEDIATELY=1 "
233 "--include-env=fileserver "
234 "--include-env=maptoguest "
235 "--include-env=simpleserver "
237 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
239 "samba-ad-member": [("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
240 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
241 ("make", "make -j", "text/plain"),
242 ("test", "make test FAIL_IMMEDIATELY=1 "
244 "--include-env=ad_member "
245 "--include-env=ad_member_idmap_rid "
246 "--include-env=ad_member_idmap_ad "
247 "--include-env=ad_member_rfc2307 "
249 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
251 "samba-ad-dc-1": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
252 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
253 ("make", "make -j", "text/plain"),
254 ("test", "make test FAIL_IMMEDIATELY=1 "
255 "TESTS='--include-env=ad_dc "
256 "--include-env=ad_dc_no_nss "
257 "--include-env=ad_dc_no_ntlm "
259 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
261 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 1 1", "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 FAIL_IMMEDIATELY=1 "
266 "--include-env=vampire_dc "
267 "--include-env=vampire_2000_dc "
268 "--include-env=rodc "
270 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
272 "samba-ad-dc-3": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
273 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
274 ("make", "make -j", "text/plain"),
275 ("test", "make test FAIL_IMMEDIATELY=1 "
277 "--include-env=promoted_dc "
278 "--include-env=chgdcpass "
279 "--include-env=preforkrestartdc "
280 "--include-env=proclimitdc "
282 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
284 "samba-ad-dc-4": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
285 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
286 ("make", "make -j", "text/plain"),
287 ("test", "make test FAIL_IMMEDIATELY=1 "
289 "--include-env=fl2000dc "
290 "--include-env=fl2003dc "
291 "--include-env=fl2008dc "
292 "--include-env=fl2008r2dc "
294 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
296 "samba-ad-dc-5": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
297 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
298 ("make", "make -j", "text/plain"),
299 ("test", "make test FAIL_IMMEDIATELY=1 "
301 "--include-env=ad_dc_default "
303 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
305 "samba-ad-dc-6": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
306 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
307 ("make", "make -j", "text/plain"),
308 ("test", "make test FAIL_IMMEDIATELY=1 "
310 "--include-env=ad_dc_slowtests "
312 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
315 "samba-schemaupgrade": [("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
316 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
317 ("make", "make -j", "text/plain"),
318 ("test", "make test FAIL_IMMEDIATELY=1 "
320 "--include-env=schema_dc "
321 "--include-env=schema_pair_dc "
323 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
325 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
326 # This is currently the longest task, so we don't randomly delay it.
327 "samba-ad-dc-ntvfs": [
328 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
329 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
330 ("make", "make -j", "text/plain"),
331 ("test", "make test FAIL_IMMEDIATELY=1 "
333 "--include-env=ad_dc_ntvfs "
335 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
337 # run the backup/restore testenvs separately as they're fairly standalone
338 # (and CI seems to max out at ~8 different DCs running at once)
339 "samba-ad-dc-backup": [("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
340 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
341 ("make", "make -j", "text/plain"),
342 ("test", "make test FAIL_IMMEDIATELY=1 "
343 "TESTS='--include-env=backupfromdc "
344 "--include-env=restoredc "
345 "--include-env=renamedc "
346 "--include-env=offlinebackupdc "
347 "--include-env=labdc "
348 "--include-env=ad_dc_backup "
350 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
352 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
353 ("make", "make -j", "text/plain"),
354 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
356 # Test cross-compile infrastructure
357 "samba-xc": [("random-sleep", "script/random-sleep.sh 900 1500", "text/plain"),
358 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
359 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
360 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params, "text/plain"),
361 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
362 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params, "text/plain"),
363 ("compare-results", "script/compare_cc_results.py "
364 "./bin/c4che/default{} "
365 "./bin-xe/c4che/default{} "
366 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3)), "text/plain")],
368 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
369 "samba-o3": [("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
370 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params, "text/plain"),
371 ("make", "make -j", "text/plain"),
372 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
373 "TESTS='--include-env=ad_dc'", "text/plain"),
374 ("install", "make install", "text/plain"),
375 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
376 ("clean", "make clean", "text/plain")],
378 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 900 1500", "text/plain"),
380 # make sure we have tdb around:
381 ("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"),
382 ("tdb-make", "cd lib/tdb && make", "text/plain"),
383 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
386 # build samba with cluster support (also building ctdb):
387 ("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"),
388 ("samba-make", "make", "text/plain"),
389 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
390 ("samba-install", "make install", "text/plain"),
391 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
394 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
395 ("clean", "make clean", "text/plain"),
396 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
399 ("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
400 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs, "text/plain"),
401 ("talloc-make", "cd lib/talloc && make", "text/plain"),
402 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
404 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs, "text/plain"),
405 ("tdb-make", "cd lib/tdb && make", "text/plain"),
406 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
408 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs, "text/plain"),
409 ("tevent-make", "cd lib/tevent && make", "text/plain"),
410 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
412 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs, "text/plain"),
413 ("ldb-make", "cd lib/ldb && make", "text/plain"),
414 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
416 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
417 ("nondevel-make", "make -j", "text/plain"),
418 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
419 ("nondevel-install", "make install", "text/plain"),
420 ("nondevel-dist", "make dist", "text/plain"),
422 # retry with all modules shared
423 ("allshared-distclean", "make distclean", "text/plain"),
424 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL", "text/plain"),
425 ("allshared-make", "make -j", "text/plain")],
428 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
429 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params, "text/plain"),
430 ("make", "make -j", "text/plain"),
431 ("test", "make test "
432 "FAIL_IMMEDIATELY=1 "
433 "TESTS='--include-env=none'",
437 ("random-sleep", "script/random-sleep.sh 1 1", "text/plain"),
438 # build with all modules static
439 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL", "text/plain"),
440 ("allstatic-make", "make -j", "text/plain"),
441 ("allstatic-test", "make test "
442 "FAIL_IMMEDIATELY=1 "
443 "TESTS='samba3.smb2.create.*nt4_dc'",
446 # retry without any required modules
447 ("none-distclean", "make distclean", "text/plain"),
448 ("none-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
449 ("none-make", "make -j", "text/plain"),
451 # retry with nonshared smbd and smbtorture
452 ("nonshared-distclean", "make distclean", "text/plain"),
453 ("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"),
454 ("nonshared-make", "make -j", "text/plain")],
456 "samba-systemkrb5": [
457 ("random-sleep", "script/random-sleep.sh 900 1500", "text/plain"),
458 ("configure", "./configure.developer " + samba_configure_params + " --with-system-mitkrb5 --with-experimental-mit-ad-dc", "text/plain"),
459 ("make", "make -j", "text/plain"),
460 # we currently cannot run a full make test, a limited list of tests could be run
461 # via "make test TESTS=sometests"
462 ("test", "make test FAIL_IMMEDIATELY=1 "
463 "TESTS='--include-env=ktest'", "text/plain"),
464 ("install", "make install", "text/plain"),
465 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
466 ("clean", "make clean", "text/plain")
469 # Test Samba without python still builds. When this test fails
470 # due to more use of Python, the expectations is that the newly
471 # failing part of the code should be disabled when
472 # --disable-python is set (rather than major work being done to
473 # support this environment). The target here is for vendors
474 # shipping a minimal smbd.
476 ("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
477 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
478 ("make", "make -j", "text/plain"),
479 ("install", "make install", "text/plain"),
480 ("find-python", "script/find_python.sh ${PREFIX}", "text/plain"),
481 ("test", "make test-nopython", "text/plain"),
482 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
483 ("clean", "make clean", "text/plain"),
485 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
486 ("talloc-make", "cd lib/talloc && make", "text/plain"),
487 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
489 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
490 ("tdb-make", "cd lib/tdb && make", "text/plain"),
491 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
493 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
494 ("tevent-make", "cd lib/tevent && make", "text/plain"),
495 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
497 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
498 ("ldb-make", "cd lib/ldb && make", "text/plain"),
499 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
501 # retry against installed library packages
502 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
503 ("libs-make", "make -j", "text/plain"),
504 ("libs-install", "make install", "text/plain"),
505 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
506 ("libs-clean", "make clean", "text/plain")
509 # check we can do the same thing using python2
510 "samba-nopython-py2": [
511 ("random-sleep", "script/random-sleep.sh 300 900", "text/plain"),
512 ("configure", "PYTHON=python2 ./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
513 ("make", "PYTHON=python2 make -j", "text/plain"),
514 ("install", "PYTHON=python2 make install", "text/plain"),
515 ("find-python", "script/find_python.sh ${PREFIX}", "text/plain"),
516 ("test", "make test-nopython", "text/plain"),
517 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
518 ("clean", "PYTHON=python2 make clean", "text/plain"),
520 ("talloc-configure", "cd lib/talloc && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
521 ("talloc-make", "cd lib/talloc && PYTHON=python2 make", "text/plain"),
522 ("talloc-install", "cd lib/talloc && PYTHON=python2 make install", "text/plain"),
524 ("tdb-configure", "cd lib/tdb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
525 ("tdb-make", "cd lib/tdb && PYTHON=python2 make", "text/plain"),
526 ("tdb-install", "cd lib/tdb && PYTHON=python2 make install", "text/plain"),
528 ("tevent-configure", "cd lib/tevent && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
529 ("tevent-make", "cd lib/tevent && PYTHON=python2 make", "text/plain"),
530 ("tevent-install", "cd lib/tevent && PYTHON=python2 make install", "text/plain"),
532 ("ldb-configure", "cd lib/ldb && PYTHON=python2 " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
533 ("ldb-make", "cd lib/ldb && PYTHON=python2 make", "text/plain"),
534 ("ldb-install", "cd lib/ldb && PYTHON=python2 make install", "text/plain"),
536 # retry against installed library packages
537 ("libs-configure", "PYTHON=python2 " + samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc", "text/plain"),
538 ("libs-make", "PYTHON=python2 make -j", "text/plain"),
539 ("libs-install", "PYTHON=python2 make install", "text/plain"),
540 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
541 ("libs-clean", "PYTHON=python2 make clean", "text/plain")
545 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
546 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
547 ("make", "make", "text/plain"),
548 ("install", "make install", "text/plain"),
549 ("test", "make test", "text/plain"),
550 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX}", "text/plain"),
551 ("make-no-lmdb", "make", "text/plain"),
552 ("install-no-lmdb", "make install", "text/plain"),
553 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
554 ("distcheck", "make distcheck", "text/plain"),
555 ("clean", "make clean", "text/plain")],
558 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
559 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
560 ("make", "make", "text/plain"),
561 ("install", "make install", "text/plain"),
562 ("test", "make test", "text/plain"),
563 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
564 ("distcheck", "make distcheck", "text/plain"),
565 ("clean", "make clean", "text/plain")],
568 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
569 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
570 ("make", "make", "text/plain"),
571 ("install", "make install", "text/plain"),
572 ("test", "make test", "text/plain"),
573 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
574 ("distcheck", "make distcheck", "text/plain"),
575 ("clean", "make clean", "text/plain")],
578 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
579 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
580 ("make", "make", "text/plain"),
581 ("install", "make install", "text/plain"),
582 ("test", "make test", "text/plain"),
583 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
584 ("distcheck", "make distcheck", "text/plain"),
585 ("clean", "make clean", "text/plain")],
588 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
589 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
590 ("make", "make", "text/plain"),
591 ("install", "make install", "text/plain"),
592 ("test", "make test", "text/plain"),
593 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
594 ("distcheck", "make distcheck", "text/plain"),
595 ("clean", "make clean", "text/plain")],
598 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
599 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
600 ("touch", "touch *.yp", "text/plain"),
601 ("make", "make", "text/plain"),
602 ("test", "make test", "text/plain"),
603 ("install", "make install", "text/plain"),
604 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
605 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
606 ("clean", "make clean", "text/plain")],
609 # these are useful for debugging autobuild
610 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
611 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
623 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
625 show = options.verbose
627 do_print("Running: '%s' in '%s'" % (cmd, dir))
629 return Popen([cmd], shell=True, stdout=PIPE, cwd=dir, close_fds=True).communicate()[0]
631 return check_call(cmd, shell=True, cwd=dir)
633 return call(cmd, shell=True, cwd=dir)
636 class builder(object):
637 '''handle build of one directory'''
639 def __init__(self, name, sequence, cp=True):
641 if name in builddirs:
642 self.dir = builddirs[name]
646 self.tag = self.name.replace('/', '_')
647 self.sequence = sequence
649 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
650 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
652 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
653 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
654 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
655 self.stdout = open(self.stdout_path, 'w')
656 self.stderr = open(self.stderr_path, 'w')
657 self.stdin = open("/dev/null", 'r')
658 self.test_source_dir = "%s/%s" % (testbase, self.tag)
659 self.prefix = "%s/%s" % (test_prefix, self.tag)
660 run_cmd("rm -rf %s" % self.test_source_dir)
661 run_cmd("rm -rf %s" % self.prefix)
663 run_cmd("cp --recursive --link --archive %s %s" % (test_master, self.test_source_dir), dir=test_master, show=True)
665 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.test_source_dir), dir=test_master, show=True)
668 def start_next(self):
669 if self.next == len(self.sequence):
670 if not options.nocleanup:
671 run_cmd("rm -rf %s" % self.test_source_dir)
672 run_cmd("rm -rf %s" % self.prefix)
673 do_print('%s: Completed OK' % self.name)
676 (self.stage, self.cmd, self.output_mime_type) = self.sequence[self.next]
677 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
678 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
679 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
680 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
681 self.cmd = self.cmd.replace("${TEST_SOURCE_DIR}", self.test_source_dir)
682 self.cmd = self.cmd.replace("${LOG_BASE}", options.log_base)
683 self.cmd = self.cmd.replace("${NAME}", self.name)
684 self.cmd = self.cmd.replace("${ENABLE_COVERAGE}", options.enable_coverage)
685 # if self.output_mime_type == "text/x-subunit":
686 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
688 os.chdir("%s/%s" % (self.test_source_dir, self.dir))
689 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, os.getcwd()))
690 self.proc = Popen(self.cmd, shell=True,
692 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
697 class buildlist(object):
698 '''handle build of multiple directories'''
700 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
703 self.tail_proc = None
706 if options.restrict_tests:
707 tasknames = ["samba-test-only"]
709 tasknames = defaulttasks
711 # If we are only running one test,
712 # do not sleep randomly to wait for it to start
713 os.environ['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
716 b = builder(n, tasks[n], cp=n is not "pidl")
719 rebase_remote = "rebaseon"
720 retry_task = [("retry",
722 git remote add -t %s %s %s
726 git describe %s/%s > old_remote_branch.desc
728 git describe %s/%s > remote_branch.desc
729 diff old_remote_branch.desc remote_branch.desc
732 rebase_branch, rebase_remote, rebase_url,
734 rebase_remote, rebase_branch,
736 rebase_remote, rebase_branch
740 self.retry = builder('retry', retry_task, cp=False)
741 self.need_retry = False
744 if self.tail_proc is not None:
745 self.tail_proc.terminate()
746 self.tail_proc.wait()
747 self.tail_proc = None
748 if self.retry is not None:
749 self.retry.proc.terminate()
750 self.retry.proc.wait()
753 if b.proc is not None:
754 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.test_source_dir, checkfail=False)
766 b.status = b.proc.poll()
772 ret = self.retry.proc.poll()
774 self.need_retry = True
784 if options.retry and self.need_retry:
786 do_print("retry needed")
787 return (0, None, None, None, "retry")
790 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
792 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
795 return (0, None, None, None, "All OK")
797 def write_system_info(self):
798 filename = 'system-info.txt'
799 f = open(filename, 'w')
800 for cmd in ['uname -a',
807 'df -m %s' % testbase]:
808 out = run_cmd(cmd, output=True, checkfail=False)
809 print('### %s' % cmd, file=f)
810 print(out.decode('utf8', 'backslashreplace'), file=f)
815 def tarlogs(self, fname):
816 tar = tarfile.open(fname, "w:gz")
818 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
819 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
820 if os.path.exists("autobuild.log"):
821 tar.add("autobuild.log")
822 sys_info = self.write_system_info()
826 def remove_logs(self):
828 os.unlink(b.stdout_path)
829 os.unlink(b.stderr_path)
831 def start_tail(self):
834 cmd.append(b.stdout_path)
835 cmd.append(b.stderr_path)
836 self.tail_proc = Popen(cmd, close_fds=True)
840 if options.nocleanup:
842 run_cmd("stat %s || true" % test_tmpdir, show=True)
843 run_cmd("stat %s" % testbase, show=True)
844 do_print("Cleaning up %r" % cleanup_list)
845 for d in cleanup_list:
846 run_cmd("rm -rf %s" % d)
849 def daemonize(logfile):
851 if pid == 0: # Parent
854 if pid != 0: # Actual daemon
859 import resource # Resource usage information.
860 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
861 if maxfd == resource.RLIM_INFINITY:
862 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
863 for fd in range(0, maxfd):
868 os.open(logfile, os.O_RDWR | os.O_CREAT)
873 def write_pidfile(fname):
874 '''write a pid file, cleanup on exit'''
875 f = open(fname, mode='w')
876 f.write("%u\n" % os.getpid())
880 def rebase_tree(rebase_url, rebase_branch="master"):
881 rebase_remote = "rebaseon"
882 do_print("Rebasing on %s" % rebase_url)
883 run_cmd("git describe HEAD", show=True, dir=test_master)
884 run_cmd("git remote add -t %s %s %s" %
885 (rebase_branch, rebase_remote, rebase_url),
886 show=True, dir=test_master)
887 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
888 if options.fix_whitespace:
889 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
890 (rebase_remote, rebase_branch),
891 show=True, dir=test_master)
893 run_cmd("git rebase --force-rebase %s/%s" %
894 (rebase_remote, rebase_branch),
895 show=True, dir=test_master)
896 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
897 (rebase_remote, rebase_branch),
898 dir=test_master, output=True)
900 do_print("No differences between HEAD and %s/%s - exiting" %
901 (rebase_remote, rebase_branch))
903 run_cmd("git describe %s/%s" %
904 (rebase_remote, rebase_branch),
905 show=True, dir=test_master)
906 run_cmd("git describe HEAD", show=True, dir=test_master)
907 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
908 (rebase_remote, rebase_branch),
909 show=True, dir=test_master)
912 def push_to(push_url, push_branch="master"):
913 push_remote = "pushto"
914 do_print("Pushing to %s" % push_url)
916 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
917 run_cmd("git commit --amend -c HEAD", dir=test_master)
918 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
919 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
920 run_cmd("git remote add -t %s %s %s" %
921 (push_branch, push_remote, push_url),
922 show=True, dir=test_master)
923 run_cmd("git push %s +HEAD:%s" %
924 (push_remote, push_branch),
925 show=True, dir=test_master)
928 def send_email(subject, text, log_tar):
929 if options.email is None:
930 do_print("not sending email because the recipient is not set")
931 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
934 outer = MIMEMultipart()
935 outer['Subject'] = subject
936 outer['To'] = options.email
937 outer['From'] = options.email_from
938 outer['Date'] = email.utils.formatdate(localtime=True)
939 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
940 outer.attach(MIMEText(text, 'plain'))
941 if options.attach_logs:
942 fp = open(log_tar, 'rb')
943 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
945 # Set the filename parameter
946 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
948 content = outer.as_string()
949 s = smtplib.SMTP(options.email_server)
950 email_user = os.getenv('SMTP_USERNAME')
951 email_password = os.getenv('SMTP_PASSWORD')
952 if email_user is not None:
954 s.login(email_user, email_password)
956 s.sendmail(options.email_from, [options.email], content)
961 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
962 elapsed_time, log_base=None, add_log_tail=True):
963 '''send an email to options.email about the failure'''
964 elapsed_minutes = elapsed_time / 60.0
970 Your autobuild on %s failed after %.1f minutes
971 when trying to test %s with the following error:
975 the autobuild has been abandoned. Please fix the error and resubmit.
977 A summary of the autobuild process is here:
980 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
982 if options.restrict_tests:
984 The build was restricted to tests matching %s\n""" % options.restrict_tests
986 if failed_task != 'rebase':
988 You can see logs of the failed task here:
993 or you can get full logs of all tasks in this job here:
997 The top commit for the tree that was built was:
1001 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
1004 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
1005 lines = f.readlines()
1006 log_tail = "".join(lines[-50:])
1007 num_lines = len(lines)
1009 # Also include stderr (compile failures) if < 50 lines of stdout
1010 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
1011 log_tail += "".join(f.readlines()[-(50 - num_lines):])
1014 The last 50 lines of log messages:
1020 logs = os.path.join(gitroot, 'logs.tar.gz')
1021 send_email('autobuild[%s] failure on %s for task %s during %s'
1022 % (options.branch, platform.node(), failed_task, failed_stage),
1026 def email_success(elapsed_time, log_base=None):
1027 '''send an email to options.email about a successful build'''
1028 if log_base is None:
1033 Your autobuild on %s has succeeded after %.1f minutes.
1035 ''' % (platform.node(), elapsed_time / 60.)
1037 if options.restrict_tests:
1039 The build was restricted to tests matching %s\n""" % options.restrict_tests
1041 if options.keeplogs:
1044 you can get full logs of all tasks in this job here:
1051 The top commit for the tree that was built was:
1054 ''' % top_commit_msg
1056 logs = os.path.join(gitroot, 'logs.tar.gz')
1057 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
1061 # get the top commit message, for emails
1062 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
1063 top_commit_msg = top_commit_msg.decode('utf-8', 'backslashreplace')
1066 os.makedirs(testbase)
1067 except Exception as reason:
1068 raise Exception("Unable to create %s : %s" % (testbase, reason))
1069 cleanup_list.append(testbase)
1072 logfile = os.path.join(testbase, "log")
1073 do_print("Forking into the background, writing progress to %s" % logfile)
1076 write_pidfile(gitroot + "/autobuild.pid")
1078 start_time = time.time()
1082 run_cmd("rm -rf %s" % test_tmpdir, show=True)
1083 os.makedirs(test_tmpdir)
1084 # The waf uninstall code removes empty directories all the way
1085 # up the tree. Creating a file in test_tmpdir stops it from
1087 run_cmd("touch %s" % os.path.join(test_tmpdir,
1088 ".directory-is-not-empty"), show=True)
1089 run_cmd("stat %s" % test_tmpdir, show=True)
1090 run_cmd("stat %s" % testbase, show=True)
1091 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
1098 if options.rebase is not None:
1099 rebase_tree(options.rebase, rebase_branch=options.branch)
1101 cleanup_list.append(gitroot + "/autobuild.pid")
1103 elapsed_time = time.time() - start_time
1104 email_failure(-1, 'rebase', 'rebase', 'rebase',
1105 'rebase on %s failed' % options.branch,
1106 elapsed_time, log_base=options.log_base)
1108 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
1111 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
1112 if status != 0 or errstr != "retry":
1119 cleanup_list.append(gitroot + "/autobuild.pid")
1125 do_print("waiting for tail to flush")
1128 elapsed_time = time.time() - start_time
1130 if options.passcmd is not None:
1131 do_print("Running passcmd: %s" % options.passcmd)
1132 run_cmd(options.passcmd, dir=test_master)
1133 if options.pushto is not None:
1134 push_to(options.pushto, push_branch=options.branch)
1135 if options.keeplogs or options.attach_logs:
1136 blist.tarlogs("logs.tar.gz")
1137 do_print("Logs in logs.tar.gz")
1138 if options.always_email:
1139 email_success(elapsed_time, log_base=options.log_base)
1145 # something failed, gather a tar of the logs
1146 blist.tarlogs("logs.tar.gz")
1148 if options.email is not None:
1149 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1150 elapsed_time, log_base=options.log_base)
1152 elapsed_minutes = elapsed_time / 60.0
1155 ####################################################################
1159 Your autobuild[%s] on %s failed after %.1f minutes
1160 when trying to test %s with the following error:
1164 the autobuild has been abandoned. Please fix the error and resubmit.
1166 ####################################################################
1168 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1172 do_print("Logs in logs.tar.gz")