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 subprocess import call, check_call, check_output, Popen, PIPE, CalledProcessError
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'
34 # allow autobuild to run within git rebase -i
35 if "GIT_DIR" in os.environ:
36 del os.environ["GIT_DIR"]
37 if "GIT_WORK_TREE" in os.environ:
38 del os.environ["GIT_WORK_TREE"]
41 '''get to the top of the git repo'''
44 if os.path.exists(os.path.join(p, ".git")):
46 p = os.path.abspath(os.path.join(p, '..'))
50 gitroot = find_git_root()
52 raise Exception("Failed to find git root")
55 def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
57 parser = OptionParser()
58 parser.add_option("--tail", help="show output while running", default=False, action="store_true")
59 parser.add_option("--keeplogs", help="keep logs", default=False, action="store_true")
60 parser.add_option("--nocleanup", help="don't remove test tree", default=False, action="store_true")
61 parser.add_option("--skip-dependencies", help="skip to run task dependency tasks", default=False, action="store_true")
62 parser.add_option("--testbase", help="base directory to run tests in (default %s)" % def_testbase,
64 parser.add_option("--full-testbase", help="full base directory to run tests in (default %s/b$PID)" % def_testbase,
66 parser.add_option("--passcmd", help="command to run on success", default=None)
67 parser.add_option("--verbose", help="show all commands as they are run",
68 default=False, action="store_true")
69 parser.add_option("--rebase", help="rebase on the given tree before testing",
70 default=None, type='str')
71 parser.add_option("--pushto", help="push to a git url on success",
72 default=None, type='str')
73 parser.add_option("--mark", help="add a Tested-By signoff before pushing",
74 default=False, action="store_true")
75 parser.add_option("--fix-whitespace", help="fix whitespace on rebase",
76 default=False, action="store_true")
77 parser.add_option("--retry", help="automatically retry if master changes",
78 default=False, action="store_true")
79 parser.add_option("--email", help="send email to the given address on failure",
80 type='str', default=None)
81 parser.add_option("--email-from", help="send email from the given address",
82 type='str', default="autobuild@samba.org")
83 parser.add_option("--email-server", help="send email via the given server",
84 type='str', default='localhost')
85 parser.add_option("--always-email", help="always send email, even on success",
87 parser.add_option("--daemon", help="daemonize after initial setup",
89 parser.add_option("--branch", help="the branch to work on (default=master)",
90 default="master", type='str')
91 parser.add_option("--log-base", help="location where the logs can be found (default=cwd)",
92 default=gitroot, type='str')
93 parser.add_option("--attach-logs", help="Attach logs to mails sent on success/failure?",
94 default=False, action="store_true")
95 parser.add_option("--restrict-tests", help="run as make test with this TESTS= regex",
97 parser.add_option("--enable-coverage", dest='enable_coverage',
98 action="store_const", const='--enable-coverage', default='',
99 help="Add --enable-coverage option while configure")
101 (options, args) = parser.parse_args()
104 if options.rebase is None:
105 raise Exception('You can only use --retry if you also rebase')
107 if options.full_testbase is not None:
108 testbase = options.full_testbase
110 testbase = "%s/b%u" % (options.testbase, os.getpid())
111 test_master = "%s/master" % testbase
112 test_prefix = "%s/prefix" % testbase
113 test_tmpdir = "%s/tmp" % testbase
114 os.environ['TMPDIR'] = test_tmpdir
116 if options.enable_coverage:
117 LCOV_CMD = "cd ${TEST_SOURCE_DIR} && lcov --capture --directory . --output-file ${LOG_BASE}/${NAME}.info --rc 'geninfo_adjust_src_path=${TEST_SOURCE_DIR}/'"
119 LCOV_CMD = 'echo "lcov skipped since no --enable-coverage specified"'
121 if options.enable_coverage:
122 PUBLISH_DOCS = "mkdir -p ${LOG_BASE}/public && mv output/htmldocs ${LOG_BASE}/public/htmldocs"
124 PUBLISH_DOCS = 'echo "HTML documentation publishing skipped since no --enable-coverage specified"'
126 CLEAN_SOURCE_TREE_CMD = "cd ${TEST_SOURCE_DIR} && script/clean-source-tree.sh"
129 def check_symbols(sofile, expected_symbols=""):
130 return "objdump --dynamic-syms " + sofile + " | " + \
131 "awk \'$0 !~ /" + expected_symbols + "/ {if ($2 == \"g\" && $3 ~ /D(F|O)/ && $4 ~ /(.bss|.text)/ && $7 !~ /(__gcov_|mangle_path)/) exit 1}\'"
134 # If we are only running specific test,
135 # do not sleep randomly to wait for it to start
136 def random_sleep(low, high):
139 def random_sleep(low, high):
140 return 'sleep {}'.format(random.randint(low, high))
148 "talloc": "lib/talloc",
149 "replace": "lib/replace",
150 "tevent": "lib/tevent",
152 "docs-xml": "docs-xml"
155 ctdb_configure_params = " --enable-developer ${PREFIX}"
156 samba_configure_params = " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data"
158 samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
159 samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
160 samba_libs_envvars += " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
161 samba_libs_configure_base = samba_libs_envvars + " ./configure --abi-check ${ENABLE_COVERAGE} --enable-debug -C ${PREFIX}"
162 samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cmocka,popt,NONE"
163 samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
164 samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs
167 def format_option(name, value=None):
168 """Format option as str list."""
169 if value is None: # boolean option
171 if not isinstance(value, list): # single value option
174 return ['{}={}'.format(name, item) for item in value]
179 INJECT_SELFTEST_PREFIX=1,
186 test_options = format_option('--include-env', include_envs)
188 test_options = format_option('--exclude-env', exclude_envs)
190 # join envs options to original test options
191 TESTS = (TESTS + ' ' + ' '.join(test_options)).strip()
195 # Allow getting a full CI with
196 # git push -o ci.variable='AUTOBUILD_FAIL_IMMEDIATELY=0'
198 FAIL_IMMEDIATELY = os.getenv("AUTOBUILD_FAIL_IMMEDIATELY", "1")
200 if int(FAIL_IMMEDIATELY):
201 _options.append('FAIL_IMMEDIATELY=1')
203 _options.append("TESTS='{}'".format(TESTS))
205 if INJECT_SELFTEST_PREFIX:
206 _options.append("TEST_OPTIONS='--with-selftest-prefix={}'".format("${SELFTEST_PREFIX}"))
207 _options.append("--directory='{}'".format("${TEST_SOURCE_DIR}"))
209 return ' '.join([cmd] + _options)
212 # When updating this list, also update .gitlab-ci.yml to add the job
213 # and to make it a dependency of 'page' for the coverage report.
218 ("random-sleep", random_sleep(300, 900)),
219 ("configure", "./configure " + ctdb_configure_params),
220 ("make", "make all"),
221 ("install", "make install"),
222 ("test", "make autotest"),
223 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
224 ("clean", "make clean"),
229 ("random-sleep", random_sleep(300, 900)),
230 ("autoconf", "autoconf"),
231 ("configure", "./configure"),
232 ("make", "make html htmlman"),
233 ("publish-docs", PUBLISH_DOCS),
234 ("clean", "make clean"),
239 "git-clone-required": True,
241 ("configure", "./configure.developer" + samba_configure_params),
243 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
244 ("chmod-R-a-w", "chmod -R a-w ."),
249 "git-clone-required": True,
251 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params),
253 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
254 ("chmod-R-a-w", "chmod -R a-w ."),
259 "git-clone-required": True,
261 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json" + samba_configure_params),
263 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
264 ("chmod-R-a-w", "chmod -R a-w ."),
269 "git-clone-required": True,
271 ("configure", "./configure.developer --without-ad-dc --with-system-heimdalkrb5" + samba_configure_params),
273 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
274 ("chmod-R-a-w", "chmod -R a-w ."),
278 "samba-without-smb1-build": {
279 "git-clone-required": True,
281 ("configure", "./configure.developer --without-smb1-server --without-ad-dc" + samba_configure_params),
283 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
284 ("chmod-R-a-w", "chmod -R a-w ."),
288 "samba-no-opath-build": {
289 "git-clone-required": True,
291 ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1' ./configure.developer --without-ad-dc " + samba_configure_params),
293 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
294 ("chmod-R-a-w", "chmod -R a-w ."),
298 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
301 ("random-sleep", random_sleep(300, 900)),
302 ("configure", "./configure.developer" + samba_configure_params),
304 ("test", make_test(exclude_envs=[
317 "ad_dc_default_smb1",
325 "ad_member_idmap_rid",
326 "admem_idmap_autorid",
327 "ad_member_idmap_ad",
335 "fileserver_smb1_done",
349 "ad_dc_default_smb1",
350 "ad_dc_default_smb1_done",
358 ("test-slow-none", make_test(cmd='make test', TESTS="--include=selftest/slow-none", include_envs=["none"])),
360 ("install", "make install"),
361 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
362 ("clean", "make clean"),
366 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
369 ("random-sleep", random_sleep(300, 900)),
370 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params),
372 ("test", make_test(exclude_envs=[
385 "ad_dc_default_smb1",
386 "ad_dc_default_smb1_done",
394 "ad_member_idmap_rid",
395 "admem_idmap_autorid",
396 "ad_member_idmap_ad",
404 "fileserver_smb1_done",
418 "ad_dc_default_smb1",
419 "ad_dc_default_smb1_done",
428 ("install", "make install"),
429 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
430 ("clean", "make clean"),
435 "dependency": "samba-nt4-build",
437 ("random-sleep", random_sleep(300, 900)),
438 ("test", make_test(include_envs=[
447 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
451 "samba-fileserver": {
452 "dependency": "samba-h5l-build",
454 ("random-sleep", random_sleep(300, 900)),
455 ("test", make_test(include_envs=[
458 "fileserver_smb1_done",
460 "ktest", # ktest is also tested in samba-ktest-mit samba
461 # and samba-mitkrb5 but is tested here against
465 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
469 "samba-fileserver-without-smb1": {
470 "dependency": "samba-without-smb1-build",
472 ("random-sleep", random_sleep(300, 900)),
473 ("test", make_test(include_envs=["fileserver"])),
475 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
479 # This is a full build without the AD DC so we test the build with
480 # MIT Kerberos from the current system. Runtime behaviour is
481 # confirmed via the ktest (static ccache and keytab) environment
485 ("random-sleep", random_sleep(300, 900)),
486 ("configure", "./configure.developer --without-ad-dc --with-system-mitkrb5 " + samba_configure_params),
488 ("test", make_test(include_envs=[
489 "ktest", # ktest is also tested in fileserver, samba and
490 # samba-mitkrb5 but is tested here against a
494 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
499 "dependency": "samba-def-build",
501 ("random-sleep", random_sleep(300, 900)),
502 ("test", make_test(include_envs=[
504 "ad_member_idmap_rid",
505 "admem_idmap_autorid",
506 "ad_member_idmap_ad",
508 "ad_member_offlogon",
511 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
516 "dependency": "samba-no-opath-build",
518 ("random-sleep", random_sleep(300, 900)),
520 cmd="make testonly DISABLE_OPATH=1",
530 ("check-clean-tree", "script/clean-source-tree.sh"),
535 "dependency": "samba-no-opath-build",
537 ("random-sleep", random_sleep(300, 900)),
539 cmd="make testonly DISABLE_OPATH=1",
543 "fileserver_smb1_done",
546 ("check-clean-tree", "script/clean-source-tree.sh"),
551 "dependency": "samba-def-build",
553 ("random-sleep", random_sleep(1, 1)),
554 ("test", make_test(include_envs=[
562 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
567 "dependency": "samba-def-build",
569 ("random-sleep", random_sleep(1, 1)),
570 ("test", make_test(include_envs=[
576 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
581 "dependency": "samba-def-build",
583 ("random-sleep", random_sleep(1, 1)),
584 ("test", make_test(include_envs=[
591 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
596 "dependency": "samba-def-build",
598 ("random-sleep", random_sleep(1, 1)),
599 ("test", make_test(include_envs=[
605 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
609 "dependency": "samba-def-build",
611 ("random-sleep", random_sleep(1, 1)),
612 ("test", make_test(include_envs=[
617 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
622 "dependency": "samba-def-build",
624 ("random-sleep", random_sleep(1, 1)),
625 ("test", make_test(include_envs=[
626 "ad_dc_default", "ad_dc_default_smb1", "ad_dc_default_smb1_done"])),
628 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
633 "dependency": "samba-def-build",
635 ("random-sleep", random_sleep(1, 1)),
636 ("test", make_test(include_envs=["ad_dc_slowtests", "ad_dc_backup"])),
638 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
642 "samba-schemaupgrade": {
643 "dependency": "samba-def-build",
645 ("random-sleep", random_sleep(1, 1)),
646 ("test", make_test(include_envs=["schema_dc", "schema_pair_dc"])),
648 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
652 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
653 # This is currently the longest task, so we don't randomly delay it.
654 "samba-ad-dc-ntvfs": {
655 "dependency": "samba-def-build",
657 ("random-sleep", random_sleep(1, 1)),
658 ("test", make_test(include_envs=["ad_dc_ntvfs"])),
660 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
664 # Test fips compliance
666 "dependency": "samba-mit-build",
668 ("random-sleep", random_sleep(1, 1)),
669 ("test", make_test(include_envs=["ad_dc_fips", "ad_member_fips"])),
670 # TODO: This seems to generate only an empty samba-fips.info ("lcov", LCOV_CMD),
671 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
675 # run the backup/restore testenvs separately as they're fairly standalone
676 # (and CI seems to max out at ~3 different DCs running at once)
678 "dependency": "samba-def-build",
680 ("random-sleep", random_sleep(300, 900)),
681 ("test", make_test(include_envs=[
687 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
691 "dependency": "samba-def-build",
693 ("random-sleep", random_sleep(300, 900)),
694 ("test", make_test(include_envs=[
700 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
705 "dependency": "samba-mit-build",
707 ("random-sleep", random_sleep(1, 1)),
708 ("test", make_test(include_envs=[
710 "ad_member_idmap_rid",
711 "admem_idmap_autorid",
712 "ad_member_idmap_ad",
714 "ad_member_offlogon",
717 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
721 "samba-addc-mit-1": {
722 "dependency": "samba-mit-build",
724 ("random-sleep", random_sleep(1, 1)),
725 ("test", make_test(include_envs=[
733 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
737 "samba-addc-mit-4a": {
738 "dependency": "samba-mit-build",
740 ("random-sleep", random_sleep(1, 1)),
741 ("test", make_test(include_envs=[
747 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
750 "samba-addc-mit-4b": {
751 "dependency": "samba-mit-build",
753 ("random-sleep", random_sleep(1, 1)),
754 ("test", make_test(include_envs=[
759 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
765 ("configure", "./configure.developer --abi-check-disable" + samba_configure_params),
767 ("test", make_test(TESTS="${TESTS}")),
772 # Test cross-compile infrastructure
775 ("random-sleep", random_sleep(900, 1500)),
776 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params),
777 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
778 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params),
779 ("verify-cross-execute-output", "grep '^Checking value of NSIG' ./bin-xe/cross-answers.txt"),
780 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
781 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params),
782 ("compare-results", "script/compare_cc_results.py "
783 "./bin/c4che/default{} "
784 "./bin-xe/c4che/default{} "
785 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX]*3))),
786 ("modify-cross-answers", "sed -i.bak -e 's/^\\(Checking value of NSIG:\\) .*/\\1 \"1234\"/' ./bin-xe/cross-answers.txt"),
787 ("configure-cross-answers-modified", "./configure.developer --out ./bin-xa2 --cross-compile" \
788 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa2/ab" + samba_configure_params),
789 ("verify-cross-answers", "test $(sed -n -e 's/VALUEOF_NSIG = \\(.*\\)/\\1/p' ./bin-xa2/c4che/default{})" \
790 " = \"'1234'\"".format(CACHE_SUFFIX)),
791 ("invalidate-cross-answers", "sed -i.bak -e '/^Checking value of NSIG/d' ./bin-xe/cross-answers.txt"),
792 ("configure-cross-answers-fail", "./configure.developer --out ./bin-xa3 --cross-compile" \
793 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa3/ab" + samba_configure_params + \
798 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
801 ("random-sleep", random_sleep(300, 900)),
802 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --abi-check-disable" + samba_configure_params),
804 ("test", make_test(cmd='make test', TESTS="--exclude=selftest/slow-none", include_envs=["none"])),
805 ("quicktest", make_test(cmd='make quicktest', include_envs=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
807 ("install", "make install"),
808 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
809 ("clean", "make clean"),
815 ("random-sleep", random_sleep(900, 1500)),
817 # make sure we have tdb around:
818 ("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}"),
819 ("tdb-make", "cd lib/tdb && make"),
820 ("tdb-install", "cd lib/tdb && make install"),
822 # build samba with cluster support (also building ctdb):
824 "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH "
825 "PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} "
826 "./configure.developer ${PREFIX} "
827 "--with-selftest-prefix=./bin/ab "
828 "--with-cluster-support "
830 "--bundled-libraries=!tdb"),
831 ("samba-make", "make"),
832 ("samba-check", "./bin/smbd --configfile=/dev/null -b | grep CLUSTER_SUPPORT"),
833 ("samba-install", "make install"),
834 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd"),
838 INJECT_SELFTEST_PREFIX=0,
839 include_envs=["clusteredmember"])
843 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
844 ("clean", "make clean"),
845 ("ctdb-clean", "cd ./ctdb && make clean"),
851 ("random-sleep", random_sleep(300, 900)),
852 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs),
853 ("talloc-make", "cd lib/talloc && make"),
854 ("talloc-install", "cd lib/talloc && make install"),
856 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs),
857 ("tdb-make", "cd lib/tdb && make"),
858 ("tdb-install", "cd lib/tdb && make install"),
860 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs),
861 ("tevent-make", "cd lib/tevent && make"),
862 ("tevent-install", "cd lib/tevent && make install"),
864 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs),
865 ("ldb-make", "cd lib/ldb && make"),
866 ("ldb-install", "cd lib/ldb && make install"),
868 ("nondevel-configure", samba_libs_envvars + " ./configure ${PREFIX}"),
869 ("nondevel-make", "make -j"),
870 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"),
871 ("nondevel-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
872 ("nondevel-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
873 ("nondevel-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
874 ("nondevel-no-libldb", "find ./bin | grep -v 'module' | grep -v 'libldbsamba' | grep 'libldb' && exit 1; exit 0"),
875 ("nondevel-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
876 ("nondevel-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
877 ("nondevel-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
878 ("nondevel-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
879 ("nondevel-no-public-nss_winbind",
880 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
881 ("nondevel-no-public-nss_wins",
882 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
883 ("nondevel-no-public-libwbclient",
884 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
885 ("nondevel-no-public-pam_winbind",
886 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
887 ("nondevel-no-public-winbind_krb5_locator",
888 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
889 ("nondevel-no-public-async_dns_krb5_locator",
890 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
891 ("nondevel-install", "make -j install"),
892 ("nondevel-dist", "make dist"),
894 ("prefix-no-private-libtalloc", "find ${PREFIX_DIR} | grep -v 'libtalloc-report' | grep 'private.*libtalloc' && exit 1; exit 0"),
895 ("prefix-no-private-libtdb", "find ${PREFIX_DIR} | grep -v 'libtdb-wrap' | grep 'private.*libtdb' && exit 1; exit 0"),
896 ("prefix-no-private-libtevent", "find ${PREFIX_DIR} | grep -v 'libtevent-util' | grep 'private.*libtevent' && exit 1; exit 0"),
897 ("prefix-no-private-libldb", "find ${PREFIX_DIR} | grep -v 'module' | grep -v 'libldbsamba' | grep 'private.*libldb' && exit 1; exit 0"),
898 ("prefix-no-samba-nss_winbind", "ldd ${PREFIX_DIR}/lib/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
899 ("prefix-no-samba-nss_wins", "ldd ${PREFIX_DIR}/lib/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
900 ("prefix-no-samba-libwbclient", "ldd ${PREFIX_DIR}/lib/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
901 ("prefix-no-samba-pam_winbind", "ldd ${PREFIX_DIR}/lib/security/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
902 ("prefix-no-public-nss_winbind",
903 check_symbols("${PREFIX_DIR}/lib/libnss_winbind.so.2", "_nss_winbind_")),
904 ("prefix-no-public-nss_wins",
905 check_symbols("${PREFIX_DIR}/lib/libnss_wins.so.2", "_nss_wins_")),
906 ("prefix-no-public-libwbclient",
907 check_symbols("${PREFIX_DIR}/lib/libwbclient.so.0", "wbc")),
908 ("prefix-no-public-pam_winbind",
909 check_symbols("${PREFIX_DIR}/lib/security/pam_winbind.so", "pam_sm_")),
910 ("prefix-no-public-winbind_krb5_locator",
911 check_symbols("${PREFIX_DIR}/lib/krb5/winbind_krb5_locator.so",
913 ("prefix-no-public-async_dns_krb5_locator",
914 check_symbols("${PREFIX_DIR}/lib/krb5/async_dns_krb5_locator.so",
917 # retry with all modules shared
918 ("allshared-distclean", "make distclean"),
919 ("allshared-configure", samba_libs_configure_samba + " --with-shared-modules=ALL"),
920 ("allshared-make", "make -j"),
921 ("allshared-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
922 ("allshared-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
923 ("allshared-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
924 ("allshared-no-libldb", "find ./bin | grep -v 'module' | grep -v 'libldbsamba' | grep 'libldb' && exit 1; exit 0"),
925 ("allshared-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
926 ("allshared-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
927 ("allshared-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
928 ("allshared-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
929 ("allshared-no-public-nss_winbind",
930 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
931 ("allshared-no-public-nss_wins",
932 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
933 ("allshared-no-public-libwbclient",
934 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
935 ("allshared-no-public-pam_winbind",
936 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
937 ("allshared-no-public-winbind_krb5_locator",
938 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
939 ("allshared-no-public-async_dns_krb5_locator",
940 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
946 # build the fuzzers (static) via the oss-fuzz script
947 ("fuzzers-mkdir-prefix", "mkdir -p ${PREFIX_DIR}"),
948 ("fuzzers-build", "OUT=${PREFIX_DIR} LIB_FUZZING_ENGINE= SANITIZER=address CXX= CFLAGS= ADDITIONAL_LDFLAGS='-fuse-ld=bfd' ./lib/fuzzing/oss-fuzz/build_samba.sh --enable-afl-fuzzer"),
952 # * Test smbd and smbtorture can build semi-static
954 # * Test Samba without python still builds.
956 # When this test fails due to more use of Python, the expectations
957 # is that the newly failing part of the code should be disabled
958 # when --disable-python is set (rather than major work being done
959 # to support this environment).
961 # The target here is for vendors shipping a minimal smbd.
962 "samba-minimal-smbd": {
964 ("random-sleep", random_sleep(300, 900)),
966 # build with all modules static
967 ("allstatic-configure", "./configure.developer " + samba_configure_params + " --with-static-modules=ALL"),
968 ("allstatic-make", "make -j"),
969 ("allstatic-test", make_test(TESTS="samba3.smb2.create.*nt4_dc")),
970 ("allstatic-lcov", LCOV_CMD),
972 # retry with nonshared smbd and smbtorture
973 ("nonshared-distclean", "make distclean"),
974 ("nonshared-configure", "./configure.developer " + samba_configure_params + " --bundled-libraries=ALL --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd"),
975 ("nonshared-make", "make -j"),
976 # TODO ("nonshared-test", make_test(TESTS="samba3.smb2.create.*nt4_dc")),
977 # TODO ("nonshared-lcov", LCOV_CMD),
979 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
980 ("clean", "make clean"),
986 ("random-sleep", random_sleep(300, 900)),
988 ("configure", "./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
990 ("find-python", "script/find_python.sh ${PREFIX}"),
991 ("test", "make test-nopython"),
993 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
994 ("clean", "make clean"),
996 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python"),
997 ("talloc-make", "cd lib/talloc && make"),
998 ("talloc-install", "cd lib/talloc && make install"),
1000 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python"),
1001 ("tdb-make", "cd lib/tdb && make"),
1002 ("tdb-install", "cd lib/tdb && make install"),
1004 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python"),
1005 ("tevent-make", "cd lib/tevent && make"),
1006 ("tevent-install", "cd lib/tevent && make install"),
1008 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base + " --bundled-libraries=cmocka,NONE --disable-python"),
1009 ("ldb-make", "cd lib/ldb && make"),
1010 ("ldb-install", "cd lib/ldb && make install"),
1012 # retry against installed library packages, but no required modules
1013 ("libs-configure", samba_libs_configure_base + samba_libs_configure_bundled_libs + " --disable-python --without-ad-dc --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT"),
1014 ("libs-make", "make -j"),
1015 ("libs-install", "make install"),
1016 ("libs-check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1017 ("libs-clean", "make clean"),
1024 ("random-sleep", random_sleep(60, 600)),
1025 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1027 ("install", "make install"),
1028 ("test", "make test"),
1030 ("clean", "make clean"),
1031 ("configure-no-lmdb", "./configure ${ENABLE_COVERAGE} --enable-developer --without-ldb-lmdb -C ${PREFIX}"),
1032 ("make-no-lmdb", "make"),
1033 ("test-no-lmdb", "make test"),
1034 ("lcov-no-lmdb", LCOV_CMD),
1035 ("install-no-lmdb", "make install"),
1036 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1037 ("distcheck", "make distcheck"),
1038 ("clean", "make clean"),
1044 ("random-sleep", random_sleep(60, 600)),
1045 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1047 ("install", "make install"),
1048 ("test", "make test"),
1050 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1051 ("distcheck", "make distcheck"),
1052 ("clean", "make clean"),
1058 ("random-sleep", random_sleep(60, 600)),
1059 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1061 ("install", "make install"),
1062 ("test", "make test"),
1064 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1065 ("distcheck", "make distcheck"),
1066 ("clean", "make clean"),
1072 ("random-sleep", random_sleep(60, 600)),
1073 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1075 ("install", "make install"),
1076 ("test", "make test"),
1078 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1079 ("distcheck", "make distcheck"),
1080 ("clean", "make clean"),
1086 ("random-sleep", random_sleep(60, 600)),
1087 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1089 ("install", "make install"),
1090 ("test", "make test"),
1092 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1093 ("distcheck", "make distcheck"),
1094 ("clean", "make clean"),
1099 "git-clone-required": True,
1101 ("random-sleep", random_sleep(60, 600)),
1102 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}"),
1103 ("touch", "touch *.yp"),
1105 ("test", "make test"),
1106 ("install", "make install"),
1107 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm"),
1108 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
1109 ("clean", "make clean"),
1113 # these are useful for debugging autobuild
1116 ("pass", 'echo passing && /bin/true'),
1121 ("fail", 'echo failing && /bin/false'),
1126 defaulttasks = list(tasks.keys())
1128 defaulttasks.remove("pass")
1129 defaulttasks.remove("fail")
1131 # The build tasks will be brought in by the test tasks as needed
1132 defaulttasks.remove("samba-def-build")
1133 defaulttasks.remove("samba-nt4-build")
1134 defaulttasks.remove("samba-mit-build")
1135 defaulttasks.remove("samba-h5l-build")
1136 defaulttasks.remove("samba-no-opath-build")
1138 # This is not a normal test, but a task to support manually running
1139 # one test under autobuild
1140 defaulttasks.remove("samba-test-only")
1142 # Only built on GitLab CI and not in the default autobuild because it
1143 # uses too much space (4GB of semi-static binaries)
1144 defaulttasks.remove("samba-fuzz")
1146 # The FIPS build runs only in GitLab CI on a current Fedora Docker
1147 # container where a simulated FIPS mode is possible.
1148 defaulttasks.remove("samba-fips")
1150 # The MIT build runs on a current Fedora where an up to date MIT KDC
1151 # is already packaged. This avoids needing to backport a current MIT
1152 # to the default Ubuntu 18.04, particularly during development, and
1153 # the need to install on the shared sn-devel-184.
1155 defaulttasks.remove("samba-mitkrb5")
1156 defaulttasks.remove("samba-admem-mit")
1157 defaulttasks.remove("samba-addc-mit-1")
1158 defaulttasks.remove("samba-addc-mit-4a")
1159 defaulttasks.remove("samba-addc-mit-4b")
1161 if os.environ.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
1162 defaulttasks.remove("samba-o3")
1171 def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
1173 show = options.verbose
1175 do_print("Running: '%s' in '%s'" % (cmd, dir))
1177 out = check_output([cmd], shell=True, cwd=dir)
1178 return out.decode(encoding='utf-8', errors='backslashreplace')
1180 return check_call(cmd, shell=True, cwd=dir)
1182 return call(cmd, shell=True, cwd=dir)
1184 def rmdir_force(dirname, re_raise=True):
1186 run_cmd("test -d %s && chmod -R +w %s; rm -rf %s" % (
1187 dirname, dirname, dirname), output=True, show=True)
1188 except CalledProcessError as e:
1189 do_print("Failed: '%s'" % (str(e)))
1190 run_cmd("tree %s" % dirname, output=True, show=True)
1196 class builder(object):
1197 '''handle build of one directory'''
1199 def __init__(self, name, definition):
1201 self.dir = builddirs.get(name, '.')
1202 self.tag = self.name.replace('/', '_')
1203 self.definition = definition
1204 self.sequence = definition["sequence"]
1205 self.git_clone_required = False
1206 if "git-clone-required" in definition:
1207 self.git_clone_required = bool(definition["git-clone-required"])
1211 self.stdout_path = "%s/%s.stdout" % (gitroot, self.tag)
1212 self.stderr_path = "%s/%s.stderr" % (gitroot, self.tag)
1214 do_print("stdout for %s in %s" % (self.name, self.stdout_path))
1215 do_print("stderr for %s in %s" % (self.name, self.stderr_path))
1216 run_cmd("rm -f %s %s" % (self.stdout_path, self.stderr_path))
1217 self.stdout = open(self.stdout_path, 'w')
1218 self.stderr = open(self.stderr_path, 'w')
1219 self.stdin = open("/dev/null", 'r')
1220 self.builder_dir = "%s/%s" % (testbase, self.tag)
1221 self.test_source_dir = self.builder_dir
1222 self.cwd = "%s/%s" % (self.builder_dir, self.dir)
1223 self.selftest_prefix = "%s/bin/ab" % (self.cwd)
1224 self.prefix = "%s/%s" % (test_prefix, self.tag)
1226 self.producer = None
1228 if self.git_clone_required:
1229 assert "dependency" not in definition
1231 def mark_existing(self):
1232 do_print('%s: Mark as existing dependency' % self.name)
1233 self.next = len(self.sequence)
1236 def add_consumer(self, consumer):
1237 do_print("%s: add consumer: %s" % (self.name, consumer.name))
1238 consumer.producer = self
1239 consumer.test_source_dir = self.test_source_dir
1240 self.consumers.append(consumer)
1242 def start_next(self):
1243 if self.producer is not None:
1244 if not self.producer.done:
1245 do_print("%s: Waiting for producer: %s" % (self.name, self.producer.name))
1249 rmdir_force(self.builder_dir)
1250 rmdir_force(self.prefix)
1251 if self.producer is not None:
1252 run_cmd("mkdir %s" % (self.builder_dir), dir=test_master, show=True)
1253 elif not self.git_clone_required:
1254 run_cmd("cp -R -a -l %s %s" % (test_master, self.builder_dir), dir=test_master, show=True)
1256 run_cmd("git clone --recursive --shared %s %s" % (test_master, self.builder_dir), dir=test_master, show=True)
1258 if self.next == len(self.sequence):
1260 do_print('%s: Completed OK' % self.name)
1262 if not options.nocleanup and len(self.consumers) == 0:
1263 do_print('%s: Cleaning up' % self.name)
1264 rmdir_force(self.builder_dir)
1265 rmdir_force(self.prefix)
1266 for consumer in self.consumers:
1267 if consumer.next != 0:
1269 do_print('%s: Starting consumer %s' % (self.name, consumer.name))
1270 consumer.start_next()
1271 if self.producer is not None:
1272 self.producer.consumers.remove(self)
1273 assert self.producer.done
1274 self.producer.start_next()
1275 do_print('%s: Remaining consumers %u' % (self.name, len(self.consumers)))
1277 (self.stage, self.cmd) = self.sequence[self.next]
1278 self.cmd = self.cmd.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific=1, standard_lib=0, prefix=self.prefix))
1279 self.cmd = self.cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
1280 self.cmd = self.cmd.replace("${PREFIX_DIR}", "%s" % self.prefix)
1281 self.cmd = self.cmd.replace("${TESTS}", options.restrict_tests)
1282 self.cmd = self.cmd.replace("${TEST_SOURCE_DIR}", self.test_source_dir)
1283 self.cmd = self.cmd.replace("${SELFTEST_PREFIX}", self.selftest_prefix)
1284 self.cmd = self.cmd.replace("${LOG_BASE}", options.log_base)
1285 self.cmd = self.cmd.replace("${NAME}", self.name)
1286 self.cmd = self.cmd.replace("${ENABLE_COVERAGE}", options.enable_coverage)
1287 do_print('%s: [%s] Running %s in %r' % (self.name, self.stage, self.cmd, self.cwd))
1288 self.proc = Popen(self.cmd, shell=True,
1289 close_fds=True, cwd=self.cwd,
1290 stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
1293 def expand_dependencies(n):
1295 if "dependency" in tasks[n]:
1296 depname = tasks[n]["dependency"]
1297 assert depname in tasks
1298 sdeps = expand_dependencies(depname)
1299 assert n not in sdeps
1302 deps.append(depname)
1306 class buildlist(object):
1307 '''handle build of multiple directories'''
1309 def __init__(self, tasknames, rebase_url, rebase_branch="master"):
1310 self.tail_proc = None
1313 if options.restrict_tests:
1314 tasknames = ["samba-test-only"]
1316 tasknames = defaulttasks
1318 given_tasknames = tasknames.copy()
1319 implicit_tasknames = []
1320 for n in given_tasknames:
1321 deps = expand_dependencies(n)
1323 if dep in given_tasknames:
1325 if dep in implicit_tasknames:
1327 implicit_tasknames.append(dep)
1329 tasknames = implicit_tasknames.copy()
1330 tasknames.extend(given_tasknames)
1331 do_print("given_tasknames: %s" % given_tasknames)
1332 do_print("implicit_tasknames: %s" % implicit_tasknames)
1333 do_print("tasknames: %s" % tasknames)
1334 self.tlist = [builder(n, tasks[n]) for n in tasknames]
1337 rebase_remote = "rebaseon"
1339 "git-clone-required": True,
1343 git remote add -t %s %s %s
1347 git describe %s/%s > old_remote_branch.desc
1349 git describe %s/%s > remote_branch.desc
1350 diff old_remote_branch.desc remote_branch.desc
1353 rebase_branch, rebase_remote, rebase_url,
1355 rebase_remote, rebase_branch,
1357 rebase_remote, rebase_branch
1360 self.retry = builder('retry', retry_task)
1361 self.need_retry = False
1363 if options.skip_dependencies:
1364 for b in self.tlist:
1365 if b.name in implicit_tasknames:
1368 for b in self.tlist:
1369 do_print("b.name=%s" % b.name)
1370 if "dependency" not in b.definition:
1372 depname = b.definition["dependency"]
1373 do_print("b.name=%s: dependency:%s" % (b.name, depname))
1374 for p in self.tlist:
1375 if p.name == depname:
1378 def kill_kids(self):
1379 if self.tail_proc is not None:
1380 self.tail_proc.terminate()
1381 self.tail_proc.wait()
1382 self.tail_proc = None
1383 if self.retry is not None:
1384 self.retry.proc.terminate()
1385 self.retry.proc.wait()
1387 for b in self.tlist:
1388 if b.proc is not None:
1389 run_cmd("killbysubdir %s > /dev/null 2>&1" % b.test_source_dir, checkfail=False)
1397 for b in self.tlist:
1400 none_running = False
1401 b.status = b.proc.poll()
1402 if b.status is None:
1407 ret = self.retry.proc.poll()
1409 self.need_retry = True
1417 for b in self.tlist:
1420 self.retry.start_next()
1423 if options.retry and self.need_retry:
1425 do_print("retry needed")
1426 return (0, None, None, None, "retry")
1429 if os.WIFSIGNALED(b.status) or os.WEXITSTATUS(b.status) != 0:
1431 return (b.status, b.name, b.stage, b.tag, "%s: [%s] failed '%s' with status %d" % (b.name, b.stage, b.cmd, b.status))
1434 return (0, None, None, None, "All OK")
1436 def write_system_info(self, filename):
1437 with open(filename, 'w') as f:
1438 for cmd in ['uname -a',
1442 'cat /proc/cpuinfo',
1445 'df -m %s' % testbase]:
1447 out = run_cmd(cmd, output=True, checkfail=False)
1448 except CalledProcessError as e:
1449 out = "<failed: %s>" % str(e)
1450 print('### %s' % cmd, file=f)
1454 def tarlogs(self, fname):
1455 with tarfile.open(fname, "w:gz") as tar:
1456 for b in self.tlist:
1457 tar.add(b.stdout_path, arcname="%s.stdout" % b.tag)
1458 tar.add(b.stderr_path, arcname="%s.stderr" % b.tag)
1459 if os.path.exists("autobuild.log"):
1460 tar.add("autobuild.log")
1461 filename = 'system-info.txt'
1462 self.write_system_info(filename)
1465 def remove_logs(self):
1466 for b in self.tlist:
1467 os.unlink(b.stdout_path)
1468 os.unlink(b.stderr_path)
1470 def start_tail(self):
1471 cmd = ["tail", "-f"]
1472 for b in self.tlist:
1473 cmd.append(b.stdout_path)
1474 cmd.append(b.stderr_path)
1475 self.tail_proc = Popen(cmd, close_fds=True)
1478 def cleanup(do_raise=False):
1479 if options.nocleanup:
1481 run_cmd("stat %s || true" % test_tmpdir, show=True)
1482 run_cmd("stat %s" % testbase, show=True)
1483 do_print("Cleaning up %r" % cleanup_list)
1484 for d in cleanup_list:
1485 ok = rmdir_force(d, re_raise=False)
1488 if os.path.isdir(d):
1489 do_print("Killing, waiting and retry")
1490 run_cmd("killbysubdir %s > /dev/null 2>&1" % d, checkfail=False)
1492 do_print("Waiting and retry")
1494 rmdir_force(d, re_raise=do_raise)
1497 def daemonize(logfile):
1499 if pid == 0: # Parent
1502 if pid != 0: # Actual daemon
1507 import resource # Resource usage information.
1508 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
1509 if maxfd == resource.RLIM_INFINITY:
1510 maxfd = 1024 # Rough guess at maximum number of open file descriptors.
1511 for fd in range(0, maxfd):
1516 os.open(logfile, os.O_RDWR | os.O_CREAT)
1521 def write_pidfile(fname):
1522 '''write a pid file, cleanup on exit'''
1523 with open(fname, mode='w') as f:
1524 f.write("%u\n" % os.getpid())
1527 def rebase_tree(rebase_url, rebase_branch="master"):
1528 rebase_remote = "rebaseon"
1529 do_print("Rebasing on %s" % rebase_url)
1530 run_cmd("git describe HEAD", show=True, dir=test_master)
1531 run_cmd("git remote add -t %s %s %s" %
1532 (rebase_branch, rebase_remote, rebase_url),
1533 show=True, dir=test_master)
1534 run_cmd("git fetch %s" % rebase_remote, show=True, dir=test_master)
1535 if options.fix_whitespace:
1536 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
1537 (rebase_remote, rebase_branch),
1538 show=True, dir=test_master)
1540 run_cmd("git rebase --force-rebase %s/%s" %
1541 (rebase_remote, rebase_branch),
1542 show=True, dir=test_master)
1543 diff = run_cmd("git --no-pager diff HEAD %s/%s" %
1544 (rebase_remote, rebase_branch),
1545 dir=test_master, output=True)
1547 do_print("No differences between HEAD and %s/%s - exiting" %
1548 (rebase_remote, rebase_branch))
1550 run_cmd("git describe %s/%s" %
1551 (rebase_remote, rebase_branch),
1552 show=True, dir=test_master)
1553 run_cmd("git describe HEAD", show=True, dir=test_master)
1554 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
1555 (rebase_remote, rebase_branch),
1556 show=True, dir=test_master)
1559 def push_to(push_url, push_branch="master"):
1560 push_remote = "pushto"
1561 do_print("Pushing to %s" % push_url)
1563 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master)
1564 run_cmd("git commit --amend -c HEAD", dir=test_master)
1565 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
1566 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
1567 run_cmd("git remote add -t %s %s %s" %
1568 (push_branch, push_remote, push_url),
1569 show=True, dir=test_master)
1570 run_cmd("git push %s +HEAD:%s" %
1571 (push_remote, push_branch),
1572 show=True, dir=test_master)
1575 def send_email(subject, text, log_tar):
1576 if options.email is None:
1577 do_print("not sending email because the recipient is not set")
1578 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
1581 outer = MIMEMultipart()
1582 outer['Subject'] = subject
1583 outer['To'] = options.email
1584 outer['From'] = options.email_from
1585 outer['Date'] = email.utils.formatdate(localtime=True)
1586 outer.preamble = 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
1587 outer.attach(MIMEText(text, 'plain', 'utf-8'))
1588 if options.attach_logs:
1589 with open(log_tar, 'rb') as fp:
1590 msg = MIMEApplication(fp.read(), 'gzip', email.encoders.encode_base64)
1591 # Set the filename parameter
1592 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(log_tar))
1594 content = outer.as_string()
1595 s = smtplib.SMTP(options.email_server)
1596 email_user = os.getenv('SMTP_USERNAME')
1597 email_password = os.getenv('SMTP_PASSWORD')
1598 if email_user is not None:
1600 s.login(email_user, email_password)
1602 s.sendmail(options.email_from, [options.email], content)
1607 def email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1608 elapsed_time, log_base=None, add_log_tail=True):
1609 '''send an email to options.email about the failure'''
1610 elapsed_minutes = elapsed_time / 60.0
1611 if log_base is None:
1616 Your autobuild on %s failed after %.1f minutes
1617 when trying to test %s with the following error:
1621 the autobuild has been abandoned. Please fix the error and resubmit.
1623 A summary of the autobuild process is here:
1626 ''' % (platform.node(), elapsed_minutes, failed_task, errstr, log_base)
1628 if options.restrict_tests:
1630 The build was restricted to tests matching %s\n""" % options.restrict_tests
1632 if failed_task != 'rebase':
1634 You can see logs of the failed task here:
1639 or you can get full logs of all tasks in this job here:
1643 The top commit for the tree that was built was:
1647 ''' % (log_base, failed_tag, log_base, failed_tag, log_base, top_commit_msg)
1650 f = open("%s/%s.stdout" % (gitroot, failed_tag), 'r')
1651 lines = f.readlines()
1652 log_tail = "".join(lines[-50:])
1653 num_lines = len(lines)
1655 # Also include stderr (compile failures) if < 50 lines of stdout
1656 f = open("%s/%s.stderr" % (gitroot, failed_tag), 'r')
1657 log_tail += "".join(f.readlines()[-(50 - num_lines):])
1660 The last 50 lines of log messages:
1666 logs = os.path.join(gitroot, 'logs.tar.gz')
1667 send_email('autobuild[%s] failure on %s for task %s during %s'
1668 % (options.branch, platform.node(), failed_task, failed_stage),
1672 def email_success(elapsed_time, log_base=None):
1673 '''send an email to options.email about a successful build'''
1674 if log_base is None:
1679 Your autobuild on %s has succeeded after %.1f minutes.
1681 ''' % (platform.node(), elapsed_time / 60.)
1683 if options.restrict_tests:
1685 The build was restricted to tests matching %s\n""" % options.restrict_tests
1687 if options.keeplogs:
1690 you can get full logs of all tasks in this job here:
1697 The top commit for the tree that was built was:
1700 ''' % top_commit_msg
1702 logs = os.path.join(gitroot, 'logs.tar.gz')
1703 send_email('autobuild[%s] success on %s' % (options.branch, platform.node()),
1707 # get the top commit message, for emails
1708 top_commit_msg = run_cmd("git log -1", dir=gitroot, output=True)
1711 if options.skip_dependencies:
1712 run_cmd("stat %s" % testbase, dir=testbase, output=True)
1714 os.makedirs(testbase)
1715 except Exception as reason:
1716 raise Exception("Unable to create %s : %s" % (testbase, reason))
1717 cleanup_list.append(testbase)
1720 logfile = os.path.join(testbase, "log")
1721 do_print("Forking into the background, writing progress to %s" % logfile)
1724 write_pidfile(gitroot + "/autobuild.pid")
1726 start_time = time.time()
1730 run_cmd("rm -rf %s" % test_tmpdir, show=True)
1731 os.makedirs(test_tmpdir)
1732 # The waf uninstall code removes empty directories all the way
1733 # up the tree. Creating a file in test_tmpdir stops it from
1735 run_cmd("touch %s" % os.path.join(test_tmpdir,
1736 ".directory-is-not-empty"), show=True)
1737 run_cmd("stat %s" % test_tmpdir, show=True)
1738 run_cmd("stat %s" % testbase, show=True)
1739 if options.skip_dependencies:
1740 run_cmd("stat %s" % test_master, dir=testbase, output=True)
1742 run_cmd("git clone --recursive --shared %s %s" % (gitroot, test_master), show=True, dir=gitroot)
1748 if options.rebase is not None:
1749 rebase_tree(options.rebase, rebase_branch=options.branch)
1751 cleanup_list.append(gitroot + "/autobuild.pid")
1753 elapsed_time = time.time() - start_time
1754 email_failure(-1, 'rebase', 'rebase', 'rebase',
1755 'rebase on %s failed' % options.branch,
1756 elapsed_time, log_base=options.log_base)
1760 blist = buildlist(args, options.rebase, rebase_branch=options.branch)
1763 (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
1764 if status != 0 or errstr != "retry":
1766 cleanup(do_raise=True)
1771 cleanup_list.append(gitroot + "/autobuild.pid")
1777 do_print("waiting for tail to flush")
1780 elapsed_time = time.time() - start_time
1782 if options.passcmd is not None:
1783 do_print("Running passcmd: %s" % options.passcmd)
1784 run_cmd(options.passcmd, dir=test_master)
1785 if options.pushto is not None:
1786 push_to(options.pushto, push_branch=options.branch)
1787 if options.keeplogs or options.attach_logs:
1788 blist.tarlogs("logs.tar.gz")
1789 do_print("Logs in logs.tar.gz")
1790 if options.always_email:
1791 email_success(elapsed_time, log_base=options.log_base)
1797 # something failed, gather a tar of the logs
1798 blist.tarlogs("logs.tar.gz")
1800 if options.email is not None:
1801 email_failure(status, failed_task, failed_stage, failed_tag, errstr,
1802 elapsed_time, log_base=options.log_base)
1804 elapsed_minutes = elapsed_time / 60.0
1807 ####################################################################
1811 Your autobuild[%s] on %s failed after %.1f minutes
1812 when trying to test %s with the following error:
1816 the autobuild has been abandoned. Please fix the error and resubmit.
1818 ####################################################################
1820 ''' % (options.branch, platform.node(), elapsed_minutes, failed_task, errstr))
1824 do_print("Logs in logs.tar.gz")