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
, check_output
, Popen
, PIPE
13 from optparse
import OptionParser
16 from email
.mime
.text
import MIMEText
17 from email
.mime
.base
import MIMEBase
18 from email
.mime
.application
import MIMEApplication
19 from email
.mime
.multipart
import MIMEMultipart
20 from distutils
.sysconfig
import get_python_lib
24 from waflib
.Build
import CACHE_SUFFIX
26 sys
.path
.insert(0, "./third_party/waf")
27 from waflib
.Build
import CACHE_SUFFIX
30 os
.environ
["PYTHONUNBUFFERED"] = "1"
32 # This speeds up testing remarkably.
33 os
.environ
['TDB_NO_FSYNC'] = '1'
37 '''get to the top of the git repo'''
40 if os
.path
.isdir(os
.path
.join(p
, ".git")):
42 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
46 gitroot
= find_git_root()
48 raise Exception("Failed to find git root")
51 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
53 parser
= OptionParser()
54 parser
.add_option("--tail", help="show output while running", default
=False, action
="store_true")
55 parser
.add_option("--keeplogs", help="keep logs", default
=False, action
="store_true")
56 parser
.add_option("--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
57 parser
.add_option("--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
59 parser
.add_option("--passcmd", help="command to run on success", default
=None)
60 parser
.add_option("--verbose", help="show all commands as they are run",
61 default
=False, action
="store_true")
62 parser
.add_option("--rebase", help="rebase on the given tree before testing",
63 default
=None, type='str')
64 parser
.add_option("--pushto", help="push to a git url on success",
65 default
=None, type='str')
66 parser
.add_option("--mark", help="add a Tested-By signoff before pushing",
67 default
=False, action
="store_true")
68 parser
.add_option("--fix-whitespace", help="fix whitespace on rebase",
69 default
=False, action
="store_true")
70 parser
.add_option("--retry", help="automatically retry if master changes",
71 default
=False, action
="store_true")
72 parser
.add_option("--email", help="send email to the given address on failure",
73 type='str', default
=None)
74 parser
.add_option("--email-from", help="send email from the given address",
75 type='str', default
="autobuild@samba.org")
76 parser
.add_option("--email-server", help="send email via the given server",
77 type='str', default
='localhost')
78 parser
.add_option("--always-email", help="always send email, even on success",
80 parser
.add_option("--daemon", help="daemonize after initial setup",
82 parser
.add_option("--branch", help="the branch to work on (default=master)",
83 default
="master", type='str')
84 parser
.add_option("--log-base", help="location where the logs can be found (default=cwd)",
85 default
=gitroot
, type='str')
86 parser
.add_option("--attach-logs", help="Attach logs to mails sent on success/failure?",
87 default
=False, action
="store_true")
88 parser
.add_option("--restrict-tests", help="run as make test with this TESTS= regex",
90 parser
.add_option("--enable-coverage", dest
='enable_coverage',
91 action
="store_const", const
='--enable-coverage', default
='',
92 help="Add --enable-coverage option while configure")
94 (options
, args
) = parser
.parse_args()
97 if options
.rebase
is None:
98 raise Exception('You can only use --retry if you also rebase')
100 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
101 test_master
= "%s/master" % testbase
102 test_prefix
= "%s/prefix" % testbase
103 test_tmpdir
= "%s/tmp" % testbase
104 os
.environ
['TMPDIR'] = test_tmpdir
106 if options
.enable_coverage
:
107 LCOV_CMD
= "cd ${TEST_SOURCE_DIR} && lcov --capture --directory . --output-file ${LOG_BASE}/${NAME}.info --rc 'geninfo_adjust_src_path=${TEST_SOURCE_DIR}/'"
109 LCOV_CMD
= 'echo "lcov skipped since no --enable-coverage specified"'
112 # If we are only running specific test,
113 # do not sleep randomly to wait for it to start
114 def random_sleep(low
, high
):
117 def random_sleep(low
, high
):
118 return 'sleep {}'.format(random
.randint(low
, high
))
126 "samba-fileserver": ".",
127 "samba-ad-member": ".",
133 "samba-none-env": ".",
134 "samba-ad-dc-1": ".",
135 "samba-ad-dc-2": ".",
136 "samba-ad-dc-3": ".",
137 "samba-ad-dc-4": ".",
138 "samba-ad-dc-5": ".",
139 "samba-ad-dc-6": ".",
140 "samba-ad-dc-ntvfs": ".",
141 "samba-ad-dc-backup": ".",
142 "samba-systemkrb5": ".",
143 "samba-nopython": ".",
144 "samba-nopython-py2": ".",
145 "samba-schemaupgrade": ".",
148 "talloc": "lib/talloc",
149 "replace": "lib/replace",
150 "tevent": "lib/tevent",
154 defaulttasks
= list(builddirs
.keys())
156 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
157 defaulttasks
.remove("samba-o3")
159 ctdb_configure_params
= " --enable-developer ${PREFIX}"
160 samba_configure_params
= " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data"
162 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
163 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
164 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
165 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check ${ENABLE_COVERAGE} --enable-debug -C ${PREFIX}"
166 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,popt,NONE"
167 samba_libs_configure_bundled_libs
= " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
168 samba_libs_configure_samba
= samba_libs_configure_base
+ samba_libs_configure_bundled_libs
171 def format_option(name
, value
=None):
172 """Format option as str list."""
173 if value
is None: # boolean option
175 if not isinstance(value
, list): # single value option
178 return ['{}={}'.format(name
, item
) for item
in value
]
190 test_options
= format_option('--include-env', include_envs
)
192 test_options
= format_option('--exclude-env', exclude_envs
)
194 # join envs options to original test options
195 TESTS
= (TESTS
+ ' ' + ' '.join(test_options
)).strip()
199 _options
.append('FAIL_IMMEDIATELY=1')
201 _options
.append("TESTS='{}'".format(TESTS
))
203 return ' '.join([cmd
] + _options
)
208 ("random-sleep", random_sleep(300, 900)),
209 ("configure", "./configure " + ctdb_configure_params
),
210 ("make", "make all"),
211 ("install", "make install"),
212 ("test", "make autotest"),
213 ("check-clean-tree", "../script/clean-source-tree.sh"),
214 ("clean", "make clean"),
217 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
219 ("random-sleep", random_sleep(300, 900)),
220 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
222 ("test", make_test(exclude_envs
=[
238 "ad_member_idmap_rid",
239 "ad_member_idmap_ad",
263 ("install", "make install"),
264 ("check-clean-tree", "script/clean-source-tree.sh"),
265 ("clean", "make clean"),
269 ("random-sleep", random_sleep(300, 900)),
270 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params
),
272 ("test", make_test(include_envs
=[
278 ("install", "make install"),
279 ("check-clean-tree", "script/clean-source-tree.sh"),
280 ("clean", "make clean"),
283 "samba-fileserver": [
284 ("random-sleep", random_sleep(300, 900)),
285 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params
),
287 ("test", make_test(include_envs
=[
293 ("check-clean-tree", "script/clean-source-tree.sh"),
297 ("random-sleep", random_sleep(300, 900)),
298 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
300 ("test", make_test(include_envs
=[
302 "ad_member_idmap_rid",
303 "ad_member_idmap_ad",
307 ("check-clean-tree", "script/clean-source-tree.sh"),
311 ("random-sleep", random_sleep(1, 1)),
312 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
314 ("test", make_test(include_envs
=[
320 ("check-clean-tree", "script/clean-source-tree.sh"),
324 ("random-sleep", random_sleep(1, 1)),
325 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
327 ("test", make_test(include_envs
=[
333 ("check-clean-tree", "script/clean-source-tree.sh"),
337 ("random-sleep", random_sleep(1, 1)),
338 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
340 ("test", make_test(include_envs
=[
347 ("check-clean-tree", "script/clean-source-tree.sh"),
351 ("random-sleep", random_sleep(1, 1)),
352 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
354 ("test", make_test(include_envs
=[
361 ("check-clean-tree", "script/clean-source-tree.sh"),
365 ("random-sleep", random_sleep(1, 1)),
366 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
368 ("test", make_test(include_envs
=["ad_dc_default"])),
370 ("check-clean-tree", "script/clean-source-tree.sh"),
374 ("random-sleep", random_sleep(1, 1)),
375 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
377 ("test", make_test(include_envs
=["ad_dc_slowtests"])),
379 ("check-clean-tree", "script/clean-source-tree.sh"),
382 "samba-schemaupgrade": [
383 ("random-sleep", random_sleep(1, 1)),
384 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
386 ("test", make_test(include_envs
=["schema_dc", "schema_pair_dc"])),
388 ("check-clean-tree", "script/clean-source-tree.sh"),
391 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
392 # This is currently the longest task, so we don't randomly delay it.
393 "samba-ad-dc-ntvfs": [
394 ("random-sleep", random_sleep(1, 1)),
395 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
397 ("test", make_test(include_envs
=["ad_dc_ntvfs"])),
399 ("check-clean-tree", "script/clean-source-tree.sh"),
402 # run the backup/restore testenvs separately as they're fairly standalone
403 # (and CI seems to max out at ~8 different DCs running at once)
404 "samba-ad-dc-backup": [
405 ("random-sleep", random_sleep(300, 900)),
406 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
408 ("test", make_test(include_envs
=[
417 ("check-clean-tree", "script/clean-source-tree.sh"),
421 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
),
423 ("test", make_test(TESTS
="${TESTS}")),
427 # Test cross-compile infrastructure
429 ("random-sleep", random_sleep(900, 1500)),
430 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
431 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
432 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
),
433 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
434 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
),
435 ("compare-results", "script/compare_cc_results.py "
436 "./bin/c4che/default{} "
437 "./bin-xe/c4che/default{} "
438 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX
]*3))),
441 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
443 ("random-sleep", random_sleep(300, 900)),
444 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
),
446 ("test", make_test(cmd
='make quicktest', include_envs
=["ad_dc"])),
448 ("install", "make install"),
449 ("check-clean-tree", "script/clean-source-tree.sh"),
450 ("clean", "make clean"),
454 ("random-sleep", random_sleep(900, 1500)),
456 # make sure we have tdb around:
457 ("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}"),
458 ("tdb-make", "cd lib/tdb && make"),
459 ("tdb-install", "cd lib/tdb && make install"),
461 # build samba with cluster support (also building ctdb):
462 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure.developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-cluster-support --bundled-libraries=!tdb"),
463 ("samba-make", "make"),
464 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT"),
465 ("samba-install", "make install"),
466 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd"),
469 ("check-clean-tree", "script/clean-source-tree.sh"),
470 ("clean", "make clean"),
471 ("ctdb-clean", "cd ./ctdb && make clean"),
475 ("random-sleep", random_sleep(300, 900)),
476 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
),
477 ("talloc-make", "cd lib/talloc && make"),
478 ("talloc-install", "cd lib/talloc && make install"),
480 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
),
481 ("tdb-make", "cd lib/tdb && make"),
482 ("tdb-install", "cd lib/tdb && make install"),
484 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
),
485 ("tevent-make", "cd lib/tevent && make"),
486 ("tevent-install", "cd lib/tevent && make install"),
488 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
),
489 ("ldb-make", "cd lib/ldb && make"),
490 ("ldb-install", "cd lib/ldb && make install"),
492 ("nondevel-configure", "./configure ${PREFIX}"),
493 ("nondevel-make", "make -j"),
494 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"),
495 ("nondevel-install", "make install"),
496 ("nondevel-dist", "make dist"),
498 # retry with all modules shared
499 ("allshared-distclean", "make distclean"),
500 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL"),
501 ("allshared-make", "make -j"),
505 ("random-sleep", random_sleep(1, 1)),
506 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
508 ("test", make_test(include_envs
=["none"])),
513 ("random-sleep", random_sleep(1, 1)),
514 # build with all modules static
515 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL"),
516 ("allstatic-make", "make -j"),
517 ("allstatic-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
520 # retry without any required modules
521 ("none-distclean", "make distclean"),
522 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT"),
523 ("none-make", "make -j"),
525 # retry with nonshared smbd and smbtorture
526 ("nonshared-distclean", "make distclean"),
527 ("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"),
528 ("nonshared-make", "make -j"),
531 "samba-systemkrb5": [
532 ("random-sleep", random_sleep(900, 1500)),
533 ("configure", "./configure.developer " + samba_configure_params
+ " --with-system-mitkrb5 --with-experimental-mit-ad-dc"),
535 # we currently cannot run a full make test, a limited list of tests could be run
536 # via "make test TESTS=sometests"
537 ("test", make_test(include_envs
=["ktest"])),
539 ("install", "make install"),
540 ("check-clean-tree", "script/clean-source-tree.sh"),
541 ("clean", "make clean"),
544 # Test Samba without python still builds. When this test fails
545 # due to more use of Python, the expectations is that the newly
546 # failing part of the code should be disabled when
547 # --disable-python is set (rather than major work being done to
548 # support this environment). The target here is for vendors
549 # shipping a minimal smbd.
551 ("random-sleep", random_sleep(300, 900)),
552 ("configure", "./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
554 ("install", "make install"),
555 ("find-python", "script/find_python.sh ${PREFIX}"),
556 ("test", "make test-nopython"),
558 ("check-clean-tree", "script/clean-source-tree.sh"),
559 ("clean", "make clean"),
561 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
562 ("talloc-make", "cd lib/talloc && make"),
563 ("talloc-install", "cd lib/talloc && make install"),
565 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
566 ("tdb-make", "cd lib/tdb && make"),
567 ("tdb-install", "cd lib/tdb && make install"),
569 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
570 ("tevent-make", "cd lib/tevent && make"),
571 ("tevent-install", "cd lib/tevent && make install"),
573 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
574 ("ldb-make", "cd lib/ldb && make"),
575 ("ldb-install", "cd lib/ldb && make install"),
577 # retry against installed library packages
578 ("libs-configure", samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " --disable-python --without-ad-dc"),
579 ("libs-make", "make -j"),
580 ("libs-install", "make install"),
581 ("libs-check-clean-tree", "script/clean-source-tree.sh"),
582 ("libs-clean", "make clean"),
585 # check we can do the same thing using python2
586 "samba-nopython-py2": [
587 ("random-sleep", random_sleep(300, 900)),
588 ("configure", "PYTHON=python2 ./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
589 ("make", "PYTHON=python2 make -j"),
590 ("install", "PYTHON=python2 make install"),
591 ("find-python", "script/find_python.sh ${PREFIX}"),
592 ("test", "make test-nopython"),
594 ("check-clean-tree", "script/clean-source-tree.sh"),
595 ("clean", "PYTHON=python2 make clean"),
597 ("talloc-configure", "cd lib/talloc && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
598 ("talloc-make", "cd lib/talloc && PYTHON=python2 make"),
599 ("talloc-install", "cd lib/talloc && PYTHON=python2 make install"),
601 ("tdb-configure", "cd lib/tdb && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
602 ("tdb-make", "cd lib/tdb && PYTHON=python2 make"),
603 ("tdb-install", "cd lib/tdb && PYTHON=python2 make install"),
605 ("tevent-configure", "cd lib/tevent && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
606 ("tevent-make", "cd lib/tevent && PYTHON=python2 make"),
607 ("tevent-install", "cd lib/tevent && PYTHON=python2 make install"),
609 ("ldb-configure", "cd lib/ldb && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
610 ("ldb-make", "cd lib/ldb && PYTHON=python2 make"),
611 ("ldb-install", "cd lib/ldb && PYTHON=python2 make install"),
613 # retry against installed library packages
614 ("libs-configure", "PYTHON=python2 " + samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " --disable-python --without-ad-dc"),
615 ("libs-make", "PYTHON=python2 make -j"),
616 ("libs-install", "PYTHON=python2 make install"),
617 ("libs-check-clean-tree", "script/clean-source-tree.sh"),
618 ("libs-clean", "PYTHON=python2 make clean"),
622 ("random-sleep", random_sleep(60, 600)),
623 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
625 ("install", "make install"),
626 ("test", "make test"),
628 ("clean", "make clean"),
629 ("configure-no-lmdb", "./configure ${ENABLE_COVERAGE} --enable-developer --without-ldb-lmdb -C ${PREFIX}"),
630 ("make-no-lmdb", "make"),
631 ("test-no-lmdb", "make test"),
632 ("lcov-no-lmdb", LCOV_CMD
),
633 ("install-no-lmdb", "make install"),
634 ("check-clean-tree", "../../script/clean-source-tree.sh"),
635 ("distcheck", "make distcheck"),
636 ("clean", "make clean"),
640 ("random-sleep", random_sleep(60, 600)),
641 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
643 ("install", "make install"),
644 ("test", "make test"),
646 ("check-clean-tree", "../../script/clean-source-tree.sh"),
647 ("distcheck", "make distcheck"),
648 ("clean", "make clean"),
652 ("random-sleep", random_sleep(60, 600)),
653 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
655 ("install", "make install"),
656 ("test", "make test"),
658 ("check-clean-tree", "../../script/clean-source-tree.sh"),
659 ("distcheck", "make distcheck"),
660 ("clean", "make clean"),
664 ("random-sleep", random_sleep(60, 600)),
665 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
667 ("install", "make install"),
668 ("test", "make test"),
670 ("check-clean-tree", "../../script/clean-source-tree.sh"),
671 ("distcheck", "make distcheck"),
672 ("clean", "make clean"),
676 ("random-sleep", random_sleep(60, 600)),
677 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
679 ("install", "make install"),
680 ("test", "make test"),
682 ("check-clean-tree", "../../script/clean-source-tree.sh"),
683 ("distcheck", "make distcheck"),
684 ("clean", "make clean"),
688 ("random-sleep", random_sleep(60, 600)),
689 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}"),
690 ("touch", "touch *.yp"),
692 ("test", "make test"),
693 ("install", "make install"),
694 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm"),
695 ("check-clean-tree", "../script/clean-source-tree.sh"),
696 ("clean", "make clean"),
699 # these are useful for debugging autobuild
700 'pass': [("pass", 'echo passing && /bin/true')],
701 'fail': [("fail", 'echo failing && /bin/false')],
711 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
713 show
= options
.verbose
715 do_print("Running: '%s' in '%s'" % (cmd
, dir))
717 out
= check_output([cmd
], shell
=True, cwd
=dir)
718 return out
.decode(encoding
='utf-8', errors
='backslashreplace')
720 return check_call(cmd
, shell
=True, cwd
=dir)
722 return call(cmd
, shell
=True, cwd
=dir)
725 class builder(object):
726 '''handle build of one directory'''
728 def __init__(self
, name
, sequence
, cp
=True):
730 self
.dir = builddirs
.get(name
, '.')
731 self
.tag
= self
.name
.replace('/', '_')
732 self
.sequence
= sequence
734 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
735 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
737 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
738 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
739 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
740 self
.stdout
= open(self
.stdout_path
, 'w')
741 self
.stderr
= open(self
.stderr_path
, 'w')
742 self
.stdin
= open("/dev/null", 'r')
743 self
.test_source_dir
= "%s/%s" % (testbase
, self
.tag
)
744 self
.cwd
= "%s/%s" % (self
.test_source_dir
, self
.dir)
745 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
746 run_cmd("rm -rf %s" % self
.test_source_dir
)
747 run_cmd("rm -rf %s" % self
.prefix
)
749 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.test_source_dir
), dir=test_master
, show
=True)
751 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.test_source_dir
), dir=test_master
, show
=True)
754 def start_next(self
):
755 if self
.next
== len(self
.sequence
):
756 if not options
.nocleanup
:
757 run_cmd("rm -rf %s" % self
.test_source_dir
)
758 run_cmd("rm -rf %s" % self
.prefix
)
759 do_print('%s: Completed OK' % self
.name
)
762 (self
.stage
, self
.cmd
) = self
.sequence
[self
.next
]
763 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific
=1, standard_lib
=0, prefix
=self
.prefix
))
764 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
765 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
766 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
767 self
.cmd
= self
.cmd
.replace("${TEST_SOURCE_DIR}", self
.test_source_dir
)
768 self
.cmd
= self
.cmd
.replace("${LOG_BASE}", options
.log_base
)
769 self
.cmd
= self
.cmd
.replace("${NAME}", self
.name
)
770 self
.cmd
= self
.cmd
.replace("${ENABLE_COVERAGE}", options
.enable_coverage
)
771 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, self
.cwd
))
772 self
.proc
= Popen(self
.cmd
, shell
=True,
773 close_fds
=True, cwd
=self
.cwd
,
774 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
778 class buildlist(object):
779 '''handle build of multiple directories'''
781 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
782 self
.tail_proc
= None
785 if options
.restrict_tests
:
786 tasknames
= ["samba-test-only"]
788 tasknames
= defaulttasks
790 self
.tlist
= [builder(n
, tasks
[n
], cp
=(n
!= "pidl")) for n
in tasknames
]
793 rebase_remote
= "rebaseon"
794 retry_task
= [("retry",
796 git remote add -t %s %s %s
800 git describe %s/%s > old_remote_branch.desc
802 git describe %s/%s > remote_branch.desc
803 diff old_remote_branch.desc remote_branch.desc
806 rebase_branch
, rebase_remote
, rebase_url
,
808 rebase_remote
, rebase_branch
,
810 rebase_remote
, rebase_branch
813 self
.retry
= builder('retry', retry_task
, cp
=False)
814 self
.need_retry
= False
817 if self
.tail_proc
is not None:
818 self
.tail_proc
.terminate()
819 self
.tail_proc
.wait()
820 self
.tail_proc
= None
821 if self
.retry
is not None:
822 self
.retry
.proc
.terminate()
823 self
.retry
.proc
.wait()
826 if b
.proc
is not None:
827 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.test_source_dir
, checkfail
=False)
839 b
.status
= b
.proc
.poll()
845 ret
= self
.retry
.proc
.poll()
847 self
.need_retry
= True
857 if options
.retry
and self
.need_retry
:
859 do_print("retry needed")
860 return (0, None, None, None, "retry")
863 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
865 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
868 return (0, None, None, None, "All OK")
870 def write_system_info(self
, filename
):
871 with
open(filename
, 'w') as f
:
872 for cmd
in ['uname -a',
879 'df -m %s' % testbase
]:
880 out
= run_cmd(cmd
, output
=True, checkfail
=False)
881 print('### %s' % cmd
, file=f
)
885 def tarlogs(self
, fname
):
886 with tarfile
.open(fname
, "w:gz") as tar
:
888 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
889 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
890 if os
.path
.exists("autobuild.log"):
891 tar
.add("autobuild.log")
892 filename
= 'system-info.txt'
893 self
.write_system_info(filename
)
896 def remove_logs(self
):
898 os
.unlink(b
.stdout_path
)
899 os
.unlink(b
.stderr_path
)
901 def start_tail(self
):
904 cmd
.append(b
.stdout_path
)
905 cmd
.append(b
.stderr_path
)
906 self
.tail_proc
= Popen(cmd
, close_fds
=True)
910 if options
.nocleanup
:
912 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
913 run_cmd("stat %s" % testbase
, show
=True)
914 do_print("Cleaning up %r" % cleanup_list
)
915 for d
in cleanup_list
:
916 run_cmd("rm -rf %s" % d
)
919 def daemonize(logfile
):
921 if pid
== 0: # Parent
924 if pid
!= 0: # Actual daemon
929 import resource
# Resource usage information.
930 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
931 if maxfd
== resource
.RLIM_INFINITY
:
932 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
933 for fd
in range(0, maxfd
):
938 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
943 def write_pidfile(fname
):
944 '''write a pid file, cleanup on exit'''
945 with
open(fname
, mode
='w') as f
:
946 f
.write("%u\n" % os
.getpid())
949 def rebase_tree(rebase_url
, rebase_branch
="master"):
950 rebase_remote
= "rebaseon"
951 do_print("Rebasing on %s" % rebase_url
)
952 run_cmd("git describe HEAD", show
=True, dir=test_master
)
953 run_cmd("git remote add -t %s %s %s" %
954 (rebase_branch
, rebase_remote
, rebase_url
),
955 show
=True, dir=test_master
)
956 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
957 if options
.fix_whitespace
:
958 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
959 (rebase_remote
, rebase_branch
),
960 show
=True, dir=test_master
)
962 run_cmd("git rebase --force-rebase %s/%s" %
963 (rebase_remote
, rebase_branch
),
964 show
=True, dir=test_master
)
965 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
966 (rebase_remote
, rebase_branch
),
967 dir=test_master
, output
=True)
969 do_print("No differences between HEAD and %s/%s - exiting" %
970 (rebase_remote
, rebase_branch
))
972 run_cmd("git describe %s/%s" %
973 (rebase_remote
, rebase_branch
),
974 show
=True, dir=test_master
)
975 run_cmd("git describe HEAD", show
=True, dir=test_master
)
976 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
977 (rebase_remote
, rebase_branch
),
978 show
=True, dir=test_master
)
981 def push_to(push_url
, push_branch
="master"):
982 push_remote
= "pushto"
983 do_print("Pushing to %s" % push_url
)
985 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
986 run_cmd("git commit --amend -c HEAD", dir=test_master
)
987 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
988 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
989 run_cmd("git remote add -t %s %s %s" %
990 (push_branch
, push_remote
, push_url
),
991 show
=True, dir=test_master
)
992 run_cmd("git push %s +HEAD:%s" %
993 (push_remote
, push_branch
),
994 show
=True, dir=test_master
)
997 def send_email(subject
, text
, log_tar
):
998 if options
.email
is None:
999 do_print("not sending email because the recipient is not set")
1000 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
1003 outer
= MIMEMultipart()
1004 outer
['Subject'] = subject
1005 outer
['To'] = options
.email
1006 outer
['From'] = options
.email_from
1007 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
1008 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
1009 outer
.attach(MIMEText(text
, 'plain'))
1010 if options
.attach_logs
:
1011 with
open(log_tar
, 'rb') as fp
:
1012 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
1013 # Set the filename parameter
1014 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
1016 content
= outer
.as_string()
1017 s
= smtplib
.SMTP(options
.email_server
)
1018 email_user
= os
.getenv('SMTP_USERNAME')
1019 email_password
= os
.getenv('SMTP_PASSWORD')
1020 if email_user
is not None:
1022 s
.login(email_user
, email_password
)
1024 s
.sendmail(options
.email_from
, [options
.email
], content
)
1029 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1030 elapsed_time
, log_base
=None, add_log_tail
=True):
1031 '''send an email to options.email about the failure'''
1032 elapsed_minutes
= elapsed_time
/ 60.0
1033 if log_base
is None:
1038 Your autobuild on %s failed after %.1f minutes
1039 when trying to test %s with the following error:
1043 the autobuild has been abandoned. Please fix the error and resubmit.
1045 A summary of the autobuild process is here:
1048 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
1050 if options
.restrict_tests
:
1052 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1054 if failed_task
!= 'rebase':
1056 You can see logs of the failed task here:
1061 or you can get full logs of all tasks in this job here:
1065 The top commit for the tree that was built was:
1069 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
1072 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
1073 lines
= f
.readlines()
1074 log_tail
= "".join(lines
[-50:])
1075 num_lines
= len(lines
)
1077 # Also include stderr (compile failures) if < 50 lines of stdout
1078 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
1079 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
1082 The last 50 lines of log messages:
1088 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1089 send_email('autobuild[%s] failure on %s for task %s during %s'
1090 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
1094 def email_success(elapsed_time
, log_base
=None):
1095 '''send an email to options.email about a successful build'''
1096 if log_base
is None:
1101 Your autobuild on %s has succeeded after %.1f minutes.
1103 ''' % (platform
.node(), elapsed_time
/ 60.)
1105 if options
.restrict_tests
:
1107 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1109 if options
.keeplogs
:
1112 you can get full logs of all tasks in this job here:
1119 The top commit for the tree that was built was:
1122 ''' % top_commit_msg
1124 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1125 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
1129 # get the top commit message, for emails
1130 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
1133 os
.makedirs(testbase
)
1134 except Exception as reason
:
1135 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
1136 cleanup_list
.append(testbase
)
1139 logfile
= os
.path
.join(testbase
, "log")
1140 do_print("Forking into the background, writing progress to %s" % logfile
)
1143 write_pidfile(gitroot
+ "/autobuild.pid")
1145 start_time
= time
.time()
1149 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
1150 os
.makedirs(test_tmpdir
)
1151 # The waf uninstall code removes empty directories all the way
1152 # up the tree. Creating a file in test_tmpdir stops it from
1154 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
1155 ".directory-is-not-empty"), show
=True)
1156 run_cmd("stat %s" % test_tmpdir
, show
=True)
1157 run_cmd("stat %s" % testbase
, show
=True)
1158 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
1164 if options
.rebase
is not None:
1165 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
1167 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1169 elapsed_time
= time
.time() - start_time
1170 email_failure(-1, 'rebase', 'rebase', 'rebase',
1171 'rebase on %s failed' % options
.branch
,
1172 elapsed_time
, log_base
=options
.log_base
)
1176 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
1179 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
1180 if status
!= 0 or errstr
!= "retry":
1187 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1193 do_print("waiting for tail to flush")
1196 elapsed_time
= time
.time() - start_time
1198 if options
.passcmd
is not None:
1199 do_print("Running passcmd: %s" % options
.passcmd
)
1200 run_cmd(options
.passcmd
, dir=test_master
)
1201 if options
.pushto
is not None:
1202 push_to(options
.pushto
, push_branch
=options
.branch
)
1203 if options
.keeplogs
or options
.attach_logs
:
1204 blist
.tarlogs("logs.tar.gz")
1205 do_print("Logs in logs.tar.gz")
1206 if options
.always_email
:
1207 email_success(elapsed_time
, log_base
=options
.log_base
)
1213 # something failed, gather a tar of the logs
1214 blist
.tarlogs("logs.tar.gz")
1216 if options
.email
is not None:
1217 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1218 elapsed_time
, log_base
=options
.log_base
)
1220 elapsed_minutes
= elapsed_time
/ 60.0
1223 ####################################################################
1227 Your autobuild[%s] on %s failed after %.1f minutes
1228 when trying to test %s with the following error:
1232 the autobuild has been abandoned. Please fix the error and resubmit.
1234 ####################################################################
1236 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1240 do_print("Logs in logs.tar.gz")