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
.exists(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 "talloc": "lib/talloc",
127 "replace": "lib/replace",
128 "tevent": "lib/tevent",
130 "docs-xml": "docs-xml"
133 ctdb_configure_params
= " --enable-developer ${PREFIX}"
134 samba_configure_params
= " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data"
136 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
137 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
138 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
139 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check ${ENABLE_COVERAGE} --enable-debug -C ${PREFIX}"
140 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,popt,NONE"
141 samba_libs_configure_bundled_libs
= " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
142 samba_libs_configure_samba
= samba_libs_configure_base
+ samba_libs_configure_bundled_libs
145 def format_option(name
, value
=None):
146 """Format option as str list."""
147 if value
is None: # boolean option
149 if not isinstance(value
, list): # single value option
152 return ['{}={}'.format(name
, item
) for item
in value
]
164 test_options
= format_option('--include-env', include_envs
)
166 test_options
= format_option('--exclude-env', exclude_envs
)
168 # join envs options to original test options
169 TESTS
= (TESTS
+ ' ' + ' '.join(test_options
)).strip()
173 _options
.append('FAIL_IMMEDIATELY=1')
175 _options
.append("TESTS='{}'".format(TESTS
))
177 return ' '.join([cmd
] + _options
)
180 # When updating this list, also update .gitlab-ci.yml to add the job
181 # and to make it a dependency of 'page' for the coverage report.
185 ("random-sleep", random_sleep(300, 900)),
186 ("configure", "./configure " + ctdb_configure_params
),
187 ("make", "make all"),
188 ("install", "make install"),
189 ("test", "make autotest"),
190 ("check-clean-tree", "../script/clean-source-tree.sh"),
191 ("clean", "make clean"),
195 ("random-sleep", random_sleep(300, 900)),
196 ("autoconf", "autoconf"),
197 ("configure", "./configure"),
198 ("make", "make html htmlman"),
199 ("clean", "make clean"),
202 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
204 ("random-sleep", random_sleep(300, 900)),
205 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
207 ("test", make_test(exclude_envs
=[
220 "ad_dc_default_smb1",
228 "ad_member_idmap_rid",
229 "ad_member_idmap_ad",
236 "fileserver_smb1_done",
250 "ad_dc_default_smb1",
251 "ad_dc_default_smb1_done",
255 "clusteredmember_smb1",
257 ("test-slow-none", make_test(cmd
='make test', TESTS
="--include=selftest/slow-none", include_envs
=["none"])),
259 ("install", "make install"),
260 ("check-clean-tree", "script/clean-source-tree.sh"),
261 ("clean", "make clean"),
264 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
266 ("random-sleep", random_sleep(300, 900)),
267 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
269 ("test", make_test(exclude_envs
=[
282 "ad_dc_default_smb1",
283 "ad_dc_default_smb1_done",
291 "ad_member_idmap_rid",
292 "ad_member_idmap_ad",
299 "fileserver_smb1_done",
313 "ad_dc_default_smb1",
314 "ad_dc_default_smb1_done",
318 "clusteredmember_smb1",
321 ("install", "make install"),
322 ("check-clean-tree", "script/clean-source-tree.sh"),
323 ("clean", "make clean"),
327 ("random-sleep", random_sleep(300, 900)),
328 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json --with-selftest-prefix=./bin/ab" + samba_configure_params
),
330 ("test", make_test(include_envs
=[
339 ("install", "make install"),
340 ("check-clean-tree", "script/clean-source-tree.sh"),
341 ("clean", "make clean"),
344 "samba-fileserver": [
345 ("random-sleep", random_sleep(300, 900)),
346 ("configure", "./configure.developer --without-ad-dc --with-system-heimdalkrb5 --with-selftest-prefix=./bin/ab" + samba_configure_params
),
348 ("test", make_test(include_envs
=[
351 "fileserver_smb1_done",
353 "ktest", # ktest is also tested in samba and samba-mitkrb5
354 # but is tested here against a system Heimdal
357 ("check-clean-tree", "script/clean-source-tree.sh"),
361 ("random-sleep", random_sleep(300, 900)),
362 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
364 ("test", make_test(include_envs
=[
366 "ad_member_idmap_rid",
367 "ad_member_idmap_ad",
371 ("check-clean-tree", "script/clean-source-tree.sh"),
375 ("random-sleep", random_sleep(1, 1)),
376 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
378 ("test", make_test(include_envs
=[
386 ("check-clean-tree", "script/clean-source-tree.sh"),
390 ("random-sleep", random_sleep(1, 1)),
391 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
393 ("test", make_test(include_envs
=[
399 ("check-clean-tree", "script/clean-source-tree.sh"),
403 ("random-sleep", random_sleep(1, 1)),
404 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
406 ("test", make_test(include_envs
=[
413 ("check-clean-tree", "script/clean-source-tree.sh"),
417 ("random-sleep", random_sleep(1, 1)),
418 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
420 ("test", make_test(include_envs
=[
427 ("check-clean-tree", "script/clean-source-tree.sh"),
431 ("random-sleep", random_sleep(1, 1)),
432 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
434 ("test", make_test(include_envs
=[
435 "ad_dc_default", "ad_dc_default_smb1", "ad_dc_default_smb1_done"])),
437 ("check-clean-tree", "script/clean-source-tree.sh"),
441 ("random-sleep", random_sleep(1, 1)),
442 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
444 ("test", make_test(include_envs
=["ad_dc_slowtests"])),
446 ("check-clean-tree", "script/clean-source-tree.sh"),
449 "samba-schemaupgrade": [
450 ("random-sleep", random_sleep(1, 1)),
451 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
453 ("test", make_test(include_envs
=["schema_dc", "schema_pair_dc"])),
455 ("check-clean-tree", "script/clean-source-tree.sh"),
458 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
459 # This is currently the longest task, so we don't randomly delay it.
460 "samba-ad-dc-ntvfs": [
461 ("random-sleep", random_sleep(1, 1)),
462 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
464 ("test", make_test(include_envs
=["ad_dc_ntvfs"])),
466 ("check-clean-tree", "script/clean-source-tree.sh"),
469 # Test fips compliance
471 ("random-sleep", random_sleep(100, 500)),
472 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
474 ("test", make_test(include_envs
=["ad_dc_fips", "ad_member_fips"])),
476 ("check-clean-tree", "script/clean-source-tree.sh"),
479 # run the backup/restore testenvs separately as they're fairly standalone
480 # (and CI seems to max out at ~8 different DCs running at once)
481 "samba-ad-dc-backup": [
482 ("random-sleep", random_sleep(300, 900)),
483 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
485 ("test", make_test(include_envs
=[
494 ("check-clean-tree", "script/clean-source-tree.sh"),
498 ("random-sleep", random_sleep(1, 1)),
499 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
501 ("test", make_test(include_envs
=[
503 "ad_member_idmap_rid",
504 "ad_member_idmap_ad",
508 ("check-clean-tree", "script/clean-source-tree.sh"),
511 "samba-ad-dc-1-mitkrb5": [
512 ("random-sleep", random_sleep(1, 1)),
513 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
515 ("test", make_test(include_envs
=[
523 ("check-clean-tree", "script/clean-source-tree.sh"),
526 "samba-ad-dc-4-mitkrb5": [
527 ("random-sleep", random_sleep(1, 1)),
528 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
530 ("test", make_test(include_envs
=[
537 ("check-clean-tree", "script/clean-source-tree.sh"),
541 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
),
543 ("test", make_test(TESTS
="${TESTS}")),
547 # Test cross-compile infrastructure
549 ("random-sleep", random_sleep(900, 1500)),
550 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
551 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
552 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
),
553 ("verify-cross-execute-output", "grep '^Checking value of NSIG' ./bin-xe/cross-answers.txt"),
554 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
555 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
),
556 ("compare-results", "script/compare_cc_results.py "
557 "./bin/c4che/default{} "
558 "./bin-xe/c4che/default{} "
559 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX
]*3))),
560 ("modify-cross-answers", "sed -i.bak -e 's/^\\(Checking value of NSIG:\\) .*/\\1 \"1234\"/' ./bin-xe/cross-answers.txt"),
561 ("configure-cross-answers-modified", "./configure.developer --out ./bin-xa2 --cross-compile" \
562 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa2/ab" + samba_configure_params
),
563 ("verify-cross-answers", "test $(sed -n -e 's/VALUEOF_NSIG = \\(.*\\)/\\1/p' ./bin-xa2/c4che/default{})" \
564 " = \"'1234'\"".format(CACHE_SUFFIX
)),
565 ("invalidate-cross-answers", "sed -i.bak -e '/^Checking value of NSIG/d' ./bin-xe/cross-answers.txt"),
566 ("configure-cross-answers-fail", "./configure.developer --out ./bin-xa3 --cross-compile" \
567 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa3/ab" + samba_configure_params
+ \
571 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
573 ("random-sleep", random_sleep(300, 900)),
574 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
),
576 ("test", make_test(cmd
='make test', TESTS
="--exclude=selftest/slow-none", include_envs
=["none"])),
577 ("quicktest", make_test(cmd
='make quicktest', include_envs
=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
579 ("install", "make install"),
580 ("check-clean-tree", "script/clean-source-tree.sh"),
581 ("clean", "make clean"),
585 ("random-sleep", random_sleep(900, 1500)),
587 # make sure we have tdb around:
588 ("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}"),
589 ("tdb-make", "cd lib/tdb && make"),
590 ("tdb-install", "cd lib/tdb && make install"),
592 # build samba with cluster support (also building ctdb):
594 "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH "
595 "PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} "
596 "./configure.developer ${PREFIX} "
597 "--with-selftest-prefix=./bin/ab "
598 "--with-cluster-support "
600 "--bundled-libraries=!tdb"),
601 ("samba-make", "make"),
602 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT"),
603 ("samba-install", "make install"),
604 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd"),
607 make_test(cmd
='make test',
608 include_envs
=["clusteredmember_smb1"])
612 ("check-clean-tree", "script/clean-source-tree.sh"),
613 ("clean", "make clean"),
614 ("ctdb-clean", "cd ./ctdb && make clean"),
618 ("random-sleep", random_sleep(300, 900)),
619 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
),
620 ("talloc-make", "cd lib/talloc && make"),
621 ("talloc-install", "cd lib/talloc && make install"),
623 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
),
624 ("tdb-make", "cd lib/tdb && make"),
625 ("tdb-install", "cd lib/tdb && make install"),
627 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
),
628 ("tevent-make", "cd lib/tevent && make"),
629 ("tevent-install", "cd lib/tevent && make install"),
631 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
),
632 ("ldb-make", "cd lib/ldb && make"),
633 ("ldb-install", "cd lib/ldb && make install"),
635 ("nondevel-configure", "./configure ${PREFIX}"),
636 ("nondevel-make", "make -j"),
637 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"),
638 ("nondevel-install", "make install"),
639 ("nondevel-dist", "make dist"),
641 # retry with all modules shared
642 ("allshared-distclean", "make distclean"),
643 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL"),
644 ("allshared-make", "make -j"),
648 ("random-sleep", random_sleep(1, 1)),
649 # build with all modules static
650 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL"),
651 ("allstatic-make", "make -j"),
652 ("allstatic-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
655 # retry without any required modules
656 ("none-distclean", "make distclean"),
657 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT"),
658 ("none-make", "make -j"),
660 # retry with nonshared smbd and smbtorture
661 ("nonshared-distclean", "make distclean"),
662 ("nonshared-configure", "./configure.developer " + samba_configure_params
+ " --bundled-libraries=ALL --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd"),
663 ("nonshared-make", "make -j")
667 # build the fuzzers (static) via the oss-fuzz script
668 ("fuzzers-mkdir-prefix", "mkdir -p ${PREFIX_DIR}"),
669 ("fuzzers-build", "OUT=${PREFIX_DIR} LIB_FUZZING_ENGINE= SANITIZER=address CXX= CFLAGS= ./lib/fuzzing/oss-fuzz/build_samba.sh --enable-afl"),
670 ("fuzzers-check", "./lib/fuzzing/oss-fuzz/check_build.sh ${PREFIX_DIR}")
673 # Test Samba without python still builds. When this test fails
674 # due to more use of Python, the expectations is that the newly
675 # failing part of the code should be disabled when
676 # --disable-python is set (rather than major work being done to
677 # support this environment). The target here is for vendors
678 # shipping a minimal smbd.
680 ("random-sleep", random_sleep(300, 900)),
681 ("configure", "./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
683 ("install", "make install"),
684 ("find-python", "script/find_python.sh ${PREFIX}"),
685 ("test", "make test-nopython"),
687 ("check-clean-tree", "script/clean-source-tree.sh"),
688 ("clean", "make clean"),
690 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
691 ("talloc-make", "cd lib/talloc && make"),
692 ("talloc-install", "cd lib/talloc && make install"),
694 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
695 ("tdb-make", "cd lib/tdb && make"),
696 ("tdb-install", "cd lib/tdb && make install"),
698 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
699 ("tevent-make", "cd lib/tevent && make"),
700 ("tevent-install", "cd lib/tevent && make install"),
702 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
703 ("ldb-make", "cd lib/ldb && make"),
704 ("ldb-install", "cd lib/ldb && make install"),
706 # retry against installed library packages
707 ("libs-configure", samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " --disable-python --without-ad-dc"),
708 ("libs-make", "make -j"),
709 ("libs-install", "make install"),
710 ("libs-check-clean-tree", "script/clean-source-tree.sh"),
711 ("libs-clean", "make clean"),
714 # check we can do the same thing using python2
715 "samba-nopython-py2": [
716 ("random-sleep", random_sleep(300, 900)),
717 ("configure", "PYTHON=python2 ./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
718 ("make", "PYTHON=python2 make -j"),
719 ("install", "PYTHON=python2 make install"),
720 ("find-python", "script/find_python.sh ${PREFIX}"),
721 ("test", "make test-nopython"),
723 ("check-clean-tree", "script/clean-source-tree.sh"),
724 ("clean", "PYTHON=python2 make clean"),
726 ("talloc-configure", "cd lib/talloc && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
727 ("talloc-make", "cd lib/talloc && PYTHON=python2 make"),
728 ("talloc-install", "cd lib/talloc && PYTHON=python2 make install"),
730 ("tdb-configure", "cd lib/tdb && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
731 ("tdb-make", "cd lib/tdb && PYTHON=python2 make"),
732 ("tdb-install", "cd lib/tdb && PYTHON=python2 make install"),
734 ("tevent-configure", "cd lib/tevent && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
735 ("tevent-make", "cd lib/tevent && PYTHON=python2 make"),
736 ("tevent-install", "cd lib/tevent && PYTHON=python2 make install"),
738 ("ldb-configure", "cd lib/ldb && PYTHON=python2 " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
739 ("ldb-make", "cd lib/ldb && PYTHON=python2 make"),
740 ("ldb-install", "cd lib/ldb && PYTHON=python2 make install"),
742 # retry against installed library packages
743 ("libs-configure", "PYTHON=python2 " + samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " --disable-python --without-ad-dc"),
744 ("libs-make", "PYTHON=python2 make -j"),
745 ("libs-install", "PYTHON=python2 make install"),
746 ("libs-check-clean-tree", "script/clean-source-tree.sh"),
747 ("libs-clean", "PYTHON=python2 make clean"),
751 ("random-sleep", random_sleep(60, 600)),
752 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
754 ("install", "make install"),
755 ("test", "make test"),
757 ("clean", "make clean"),
758 ("configure-no-lmdb", "./configure ${ENABLE_COVERAGE} --enable-developer --without-ldb-lmdb -C ${PREFIX}"),
759 ("make-no-lmdb", "make"),
760 ("test-no-lmdb", "make test"),
761 ("lcov-no-lmdb", LCOV_CMD
),
762 ("install-no-lmdb", "make install"),
763 ("check-clean-tree", "../../script/clean-source-tree.sh"),
764 ("distcheck", "make distcheck"),
765 ("clean", "make clean"),
769 ("random-sleep", random_sleep(60, 600)),
770 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
772 ("install", "make install"),
773 ("test", "make test"),
775 ("check-clean-tree", "../../script/clean-source-tree.sh"),
776 ("distcheck", "make distcheck"),
777 ("clean", "make clean"),
781 ("random-sleep", random_sleep(60, 600)),
782 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
784 ("install", "make install"),
785 ("test", "make test"),
787 ("check-clean-tree", "../../script/clean-source-tree.sh"),
788 ("distcheck", "make distcheck"),
789 ("clean", "make clean"),
793 ("random-sleep", random_sleep(60, 600)),
794 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
796 ("install", "make install"),
797 ("test", "make test"),
799 ("check-clean-tree", "../../script/clean-source-tree.sh"),
800 ("distcheck", "make distcheck"),
801 ("clean", "make clean"),
805 ("random-sleep", random_sleep(60, 600)),
806 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
808 ("install", "make install"),
809 ("test", "make test"),
811 ("check-clean-tree", "../../script/clean-source-tree.sh"),
812 ("distcheck", "make distcheck"),
813 ("clean", "make clean"),
817 ("random-sleep", random_sleep(60, 600)),
818 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}"),
819 ("touch", "touch *.yp"),
821 ("test", "make test"),
822 ("install", "make install"),
823 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm"),
824 ("check-clean-tree", "../script/clean-source-tree.sh"),
825 ("clean", "make clean"),
828 # these are useful for debugging autobuild
829 'pass': [("pass", 'echo passing && /bin/true')],
830 'fail': [("fail", 'echo failing && /bin/false')],
833 defaulttasks
= list(tasks
.keys())
835 defaulttasks
.remove("pass")
836 defaulttasks
.remove("fail")
837 defaulttasks
.remove("samba-test-only")
838 defaulttasks
.remove("samba-fuzz")
839 defaulttasks
.remove("samba-fips")
840 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
841 defaulttasks
.remove("samba-o3")
850 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
852 show
= options
.verbose
854 do_print("Running: '%s' in '%s'" % (cmd
, dir))
856 out
= check_output([cmd
], shell
=True, cwd
=dir)
857 return out
.decode(encoding
='utf-8', errors
='backslashreplace')
859 return check_call(cmd
, shell
=True, cwd
=dir)
861 return call(cmd
, shell
=True, cwd
=dir)
864 class builder(object):
865 '''handle build of one directory'''
867 def __init__(self
, name
, sequence
, cp
=True):
869 self
.dir = builddirs
.get(name
, '.')
870 self
.tag
= self
.name
.replace('/', '_')
871 self
.sequence
= sequence
873 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
874 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
876 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
877 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
878 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
879 self
.stdout
= open(self
.stdout_path
, 'w')
880 self
.stderr
= open(self
.stderr_path
, 'w')
881 self
.stdin
= open("/dev/null", 'r')
882 self
.test_source_dir
= "%s/%s" % (testbase
, self
.tag
)
883 self
.cwd
= "%s/%s" % (self
.test_source_dir
, self
.dir)
884 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
885 run_cmd("rm -rf %s" % self
.test_source_dir
)
886 run_cmd("rm -rf %s" % self
.prefix
)
888 run_cmd("cp -R -a -l %s %s" % (test_master
, self
.test_source_dir
), dir=test_master
, show
=True)
890 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.test_source_dir
), dir=test_master
, show
=True)
893 def start_next(self
):
894 if self
.next
== len(self
.sequence
):
895 if not options
.nocleanup
:
896 run_cmd("rm -rf %s" % self
.test_source_dir
)
897 run_cmd("rm -rf %s" % self
.prefix
)
898 do_print('%s: Completed OK' % self
.name
)
901 (self
.stage
, self
.cmd
) = self
.sequence
[self
.next
]
902 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific
=1, standard_lib
=0, prefix
=self
.prefix
))
903 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
904 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
905 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
906 self
.cmd
= self
.cmd
.replace("${TEST_SOURCE_DIR}", self
.test_source_dir
)
907 self
.cmd
= self
.cmd
.replace("${LOG_BASE}", options
.log_base
)
908 self
.cmd
= self
.cmd
.replace("${NAME}", self
.name
)
909 self
.cmd
= self
.cmd
.replace("${ENABLE_COVERAGE}", options
.enable_coverage
)
910 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, self
.cwd
))
911 self
.proc
= Popen(self
.cmd
, shell
=True,
912 close_fds
=True, cwd
=self
.cwd
,
913 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
917 class buildlist(object):
918 '''handle build of multiple directories'''
920 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
921 self
.tail_proc
= None
924 if options
.restrict_tests
:
925 tasknames
= ["samba-test-only"]
927 tasknames
= defaulttasks
929 self
.tlist
= [builder(n
, tasks
[n
], cp
=(n
!= "pidl")) for n
in tasknames
]
932 rebase_remote
= "rebaseon"
933 retry_task
= [("retry",
935 git remote add -t %s %s %s
939 git describe %s/%s > old_remote_branch.desc
941 git describe %s/%s > remote_branch.desc
942 diff old_remote_branch.desc remote_branch.desc
945 rebase_branch
, rebase_remote
, rebase_url
,
947 rebase_remote
, rebase_branch
,
949 rebase_remote
, rebase_branch
952 self
.retry
= builder('retry', retry_task
, cp
=False)
953 self
.need_retry
= False
956 if self
.tail_proc
is not None:
957 self
.tail_proc
.terminate()
958 self
.tail_proc
.wait()
959 self
.tail_proc
= None
960 if self
.retry
is not None:
961 self
.retry
.proc
.terminate()
962 self
.retry
.proc
.wait()
965 if b
.proc
is not None:
966 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.test_source_dir
, checkfail
=False)
978 b
.status
= b
.proc
.poll()
984 ret
= self
.retry
.proc
.poll()
986 self
.need_retry
= True
996 if options
.retry
and self
.need_retry
:
998 do_print("retry needed")
999 return (0, None, None, None, "retry")
1002 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
1004 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
1007 return (0, None, None, None, "All OK")
1009 def write_system_info(self
, filename
):
1010 with
open(filename
, 'w') as f
:
1011 for cmd
in ['uname -a',
1015 'cat /proc/cpuinfo',
1018 'df -m %s' % testbase
]:
1020 out
= run_cmd(cmd
, output
=True, checkfail
=False)
1021 except subprocess
.CalledProcessError
as e
:
1022 out
= "<failed: %s>" % str(e
)
1023 print('### %s' % cmd
, file=f
)
1027 def tarlogs(self
, fname
):
1028 with tarfile
.open(fname
, "w:gz") as tar
:
1029 for b
in self
.tlist
:
1030 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
1031 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
1032 if os
.path
.exists("autobuild.log"):
1033 tar
.add("autobuild.log")
1034 filename
= 'system-info.txt'
1035 self
.write_system_info(filename
)
1038 def remove_logs(self
):
1039 for b
in self
.tlist
:
1040 os
.unlink(b
.stdout_path
)
1041 os
.unlink(b
.stderr_path
)
1043 def start_tail(self
):
1044 cmd
= ["tail", "-f"]
1045 for b
in self
.tlist
:
1046 cmd
.append(b
.stdout_path
)
1047 cmd
.append(b
.stderr_path
)
1048 self
.tail_proc
= Popen(cmd
, close_fds
=True)
1052 if options
.nocleanup
:
1054 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
1055 run_cmd("stat %s" % testbase
, show
=True)
1056 do_print("Cleaning up %r" % cleanup_list
)
1057 for d
in cleanup_list
:
1058 run_cmd("rm -rf %s" % d
)
1061 def daemonize(logfile
):
1063 if pid
== 0: # Parent
1066 if pid
!= 0: # Actual daemon
1071 import resource
# Resource usage information.
1072 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
1073 if maxfd
== resource
.RLIM_INFINITY
:
1074 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
1075 for fd
in range(0, maxfd
):
1080 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
1085 def write_pidfile(fname
):
1086 '''write a pid file, cleanup on exit'''
1087 with
open(fname
, mode
='w') as f
:
1088 f
.write("%u\n" % os
.getpid())
1091 def rebase_tree(rebase_url
, rebase_branch
="master"):
1092 rebase_remote
= "rebaseon"
1093 do_print("Rebasing on %s" % rebase_url
)
1094 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1095 run_cmd("git remote add -t %s %s %s" %
1096 (rebase_branch
, rebase_remote
, rebase_url
),
1097 show
=True, dir=test_master
)
1098 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
1099 if options
.fix_whitespace
:
1100 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
1101 (rebase_remote
, rebase_branch
),
1102 show
=True, dir=test_master
)
1104 run_cmd("git rebase --force-rebase %s/%s" %
1105 (rebase_remote
, rebase_branch
),
1106 show
=True, dir=test_master
)
1107 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
1108 (rebase_remote
, rebase_branch
),
1109 dir=test_master
, output
=True)
1111 do_print("No differences between HEAD and %s/%s - exiting" %
1112 (rebase_remote
, rebase_branch
))
1114 run_cmd("git describe %s/%s" %
1115 (rebase_remote
, rebase_branch
),
1116 show
=True, dir=test_master
)
1117 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1118 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
1119 (rebase_remote
, rebase_branch
),
1120 show
=True, dir=test_master
)
1123 def push_to(push_url
, push_branch
="master"):
1124 push_remote
= "pushto"
1125 do_print("Pushing to %s" % push_url
)
1127 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
1128 run_cmd("git commit --amend -c HEAD", dir=test_master
)
1129 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
1130 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
1131 run_cmd("git remote add -t %s %s %s" %
1132 (push_branch
, push_remote
, push_url
),
1133 show
=True, dir=test_master
)
1134 run_cmd("git push %s +HEAD:%s" %
1135 (push_remote
, push_branch
),
1136 show
=True, dir=test_master
)
1139 def send_email(subject
, text
, log_tar
):
1140 if options
.email
is None:
1141 do_print("not sending email because the recipient is not set")
1142 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
1145 outer
= MIMEMultipart()
1146 outer
['Subject'] = subject
1147 outer
['To'] = options
.email
1148 outer
['From'] = options
.email_from
1149 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
1150 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
1151 outer
.attach(MIMEText(text
, 'plain'))
1152 if options
.attach_logs
:
1153 with
open(log_tar
, 'rb') as fp
:
1154 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
1155 # Set the filename parameter
1156 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
1158 content
= outer
.as_string()
1159 s
= smtplib
.SMTP(options
.email_server
)
1160 email_user
= os
.getenv('SMTP_USERNAME')
1161 email_password
= os
.getenv('SMTP_PASSWORD')
1162 if email_user
is not None:
1164 s
.login(email_user
, email_password
)
1166 s
.sendmail(options
.email_from
, [options
.email
], content
)
1171 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1172 elapsed_time
, log_base
=None, add_log_tail
=True):
1173 '''send an email to options.email about the failure'''
1174 elapsed_minutes
= elapsed_time
/ 60.0
1175 if log_base
is None:
1180 Your autobuild on %s failed after %.1f minutes
1181 when trying to test %s with the following error:
1185 the autobuild has been abandoned. Please fix the error and resubmit.
1187 A summary of the autobuild process is here:
1190 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
1192 if options
.restrict_tests
:
1194 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1196 if failed_task
!= 'rebase':
1198 You can see logs of the failed task here:
1203 or you can get full logs of all tasks in this job here:
1207 The top commit for the tree that was built was:
1211 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
1214 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
1215 lines
= f
.readlines()
1216 log_tail
= "".join(lines
[-50:])
1217 num_lines
= len(lines
)
1219 # Also include stderr (compile failures) if < 50 lines of stdout
1220 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
1221 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
1224 The last 50 lines of log messages:
1230 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1231 send_email('autobuild[%s] failure on %s for task %s during %s'
1232 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
1236 def email_success(elapsed_time
, log_base
=None):
1237 '''send an email to options.email about a successful build'''
1238 if log_base
is None:
1243 Your autobuild on %s has succeeded after %.1f minutes.
1245 ''' % (platform
.node(), elapsed_time
/ 60.)
1247 if options
.restrict_tests
:
1249 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1251 if options
.keeplogs
:
1254 you can get full logs of all tasks in this job here:
1261 The top commit for the tree that was built was:
1264 ''' % top_commit_msg
1266 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1267 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
1271 # get the top commit message, for emails
1272 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
1275 os
.makedirs(testbase
)
1276 except Exception as reason
:
1277 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
1278 cleanup_list
.append(testbase
)
1281 logfile
= os
.path
.join(testbase
, "log")
1282 do_print("Forking into the background, writing progress to %s" % logfile
)
1285 write_pidfile(gitroot
+ "/autobuild.pid")
1287 start_time
= time
.time()
1291 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
1292 os
.makedirs(test_tmpdir
)
1293 # The waf uninstall code removes empty directories all the way
1294 # up the tree. Creating a file in test_tmpdir stops it from
1296 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
1297 ".directory-is-not-empty"), show
=True)
1298 run_cmd("stat %s" % test_tmpdir
, show
=True)
1299 run_cmd("stat %s" % testbase
, show
=True)
1300 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
1306 if options
.rebase
is not None:
1307 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
1309 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1311 elapsed_time
= time
.time() - start_time
1312 email_failure(-1, 'rebase', 'rebase', 'rebase',
1313 'rebase on %s failed' % options
.branch
,
1314 elapsed_time
, log_base
=options
.log_base
)
1318 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
1321 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
1322 if status
!= 0 or errstr
!= "retry":
1329 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1335 do_print("waiting for tail to flush")
1338 elapsed_time
= time
.time() - start_time
1340 if options
.passcmd
is not None:
1341 do_print("Running passcmd: %s" % options
.passcmd
)
1342 run_cmd(options
.passcmd
, dir=test_master
)
1343 if options
.pushto
is not None:
1344 push_to(options
.pushto
, push_branch
=options
.branch
)
1345 if options
.keeplogs
or options
.attach_logs
:
1346 blist
.tarlogs("logs.tar.gz")
1347 do_print("Logs in logs.tar.gz")
1348 if options
.always_email
:
1349 email_success(elapsed_time
, log_base
=options
.log_base
)
1355 # something failed, gather a tar of the logs
1356 blist
.tarlogs("logs.tar.gz")
1358 if options
.email
is not None:
1359 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1360 elapsed_time
, log_base
=options
.log_base
)
1362 elapsed_minutes
= elapsed_time
/ 60.0
1365 ####################################################################
1369 Your autobuild[%s] on %s failed after %.1f minutes
1370 when trying to test %s with the following error:
1374 the autobuild has been abandoned. Please fix the error and resubmit.
1376 ####################################################################
1378 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1382 do_print("Logs in logs.tar.gz")