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 sysconfig
import get_path
25 from waflib
.Build
import CACHE_SUFFIX
27 sys
.path
.insert(0, "./third_party/waf")
28 from waflib
.Build
import CACHE_SUFFIX
30 logging
.basicConfig(format
='%(asctime)s %(message)s')
31 logger
= logging
.getLogger('autobuild')
32 logger
.setLevel(logging
.INFO
)
34 os
.environ
["PYTHONUNBUFFERED"] = "1"
36 # This speeds up testing remarkably.
37 os
.environ
['TDB_NO_FSYNC'] = '1'
39 # allow autobuild to run within git rebase -i
40 if "GIT_DIR" in os
.environ
:
41 del os
.environ
["GIT_DIR"]
42 if "GIT_WORK_TREE" in os
.environ
:
43 del os
.environ
["GIT_WORK_TREE"]
46 '''get to the top of the git repo'''
49 if os
.path
.exists(os
.path
.join(p
, ".git")):
51 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
55 gitroot
= find_git_root()
57 raise Exception("Failed to find git root")
60 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
62 parser
= OptionParser()
63 parser
.add_option("--tail", help="show output while running", default
=False, action
="store_true")
64 parser
.add_option("--keeplogs", help="keep logs", default
=False, action
="store_true")
65 parser
.add_option("--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
66 parser
.add_option("--skip-dependencies", help="skip to run task dependency tasks", default
=False, action
="store_true")
67 parser
.add_option("--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
69 parser
.add_option("--full-testbase", help="full base directory to run tests in (default %s/b$PID)" % def_testbase
,
71 parser
.add_option("--passcmd", help="command to run on success", default
=None)
72 parser
.add_option("--verbose", help="show all commands as they are run",
73 default
=False, action
="store_true")
74 parser
.add_option("--rebase", help="rebase on the given tree before testing",
75 default
=None, type='str')
76 parser
.add_option("--pushto", help="push to a git url on success",
77 default
=None, type='str')
78 parser
.add_option("--mark", help="add a Tested-By signoff before pushing",
79 default
=False, action
="store_true")
80 parser
.add_option("--fix-whitespace", help="fix whitespace on rebase",
81 default
=False, action
="store_true")
82 parser
.add_option("--retry", help="automatically retry if master changes",
83 default
=False, action
="store_true")
84 parser
.add_option("--email", help="send email to the given address on failure",
85 type='str', default
=None)
86 parser
.add_option("--email-from", help="send email from the given address",
87 type='str', default
="autobuild@samba.org")
88 parser
.add_option("--email-server", help="send email via the given server",
89 type='str', default
='localhost')
90 parser
.add_option("--always-email", help="always send email, even on success",
92 parser
.add_option("--daemon", help="daemonize after initial setup",
94 parser
.add_option("--branch", help="the branch to work on (default=master)",
95 default
="master", type='str')
96 parser
.add_option("--log-base", help="location where the logs can be found (default=cwd)",
97 default
=gitroot
, type='str')
98 parser
.add_option("--attach-logs", help="Attach logs to mails sent on success/failure?",
99 default
=False, action
="store_true")
100 parser
.add_option("--restrict-tests", help="run as make test with this TESTS= regex",
102 parser
.add_option("--enable-coverage", dest
='enable_coverage',
103 action
="store_const", const
='--enable-coverage', default
='',
104 help="Add --enable-coverage option while configure")
106 (options
, args
) = parser
.parse_args()
109 if options
.rebase
is None:
110 raise Exception('You can only use --retry if you also rebase')
113 logger
.setLevel(logging
.DEBUG
)
115 if options
.full_testbase
is not None:
116 testbase
= options
.full_testbase
118 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
119 test_master
= "%s/master" % testbase
120 test_prefix
= "%s/prefix" % testbase
121 test_tmpdir
= "%s/tmp" % testbase
122 os
.environ
['TMPDIR'] = test_tmpdir
124 if options
.enable_coverage
:
125 LCOV_CMD
= "cd ${TEST_SOURCE_DIR} && lcov --capture --directory . --output-file ${LOG_BASE}/${NAME}.info --rc 'geninfo_adjust_src_path=${TEST_SOURCE_DIR}/'"
127 LCOV_CMD
= 'echo "lcov skipped since no --enable-coverage specified"'
129 if options
.enable_coverage
:
130 PUBLISH_DOCS
= "mkdir -p ${LOG_BASE}/public && mv output/htmldocs ${LOG_BASE}/public/htmldocs"
132 PUBLISH_DOCS
= 'echo "HTML documentation publishing skipped since no --enable-coverage specified"'
134 CLEAN_SOURCE_TREE_CMD
= "cd ${TEST_SOURCE_DIR} && script/clean-source-tree.sh"
137 def check_symbols(sofile
, expected_symbols
=""):
138 return "objdump --dynamic-syms " + sofile
+ " | " + \
139 "awk \'$0 !~ /" + expected_symbols
+ "/ {if ($2 == \"g\" && $3 ~ /D(F|O)/ && $4 ~ /(.bss|.text)/ && $7 !~ /(__gcov_|mangle_path)/) exit 1}\'"
142 # If we are only running specific test,
143 # do not sleep randomly to wait for it to start
144 def random_sleep(low
, high
):
147 def random_sleep(low
, high
):
148 return 'sleep {}'.format(random
.randint(low
, high
))
155 "talloc": "lib/talloc",
156 "replace": "lib/replace",
157 "tevent": "lib/tevent",
159 "docs-xml": "docs-xml"
162 ctdb_configure_params
= " --enable-developer ${PREFIX}"
163 samba_configure_params
= " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data"
165 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
166 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
167 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
168 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check ${ENABLE_COVERAGE} --enable-debug -C ${PREFIX}"
169 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,popt,NONE"
170 samba_libs_configure_bundled_libs
= " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!tevent,!pytevent,!popt"
171 samba_libs_configure_samba
= samba_libs_configure_base
+ samba_libs_configure_bundled_libs
174 def format_option(name
, value
=None):
175 """Format option as str list."""
176 if value
is None: # boolean option
178 if not isinstance(value
, list): # single value option
181 return ['{}={}'.format(name
, item
) for item
in value
]
186 INJECT_SELFTEST_PREFIX
=1,
193 test_options
= format_option('--include-env', include_envs
)
195 test_options
= format_option('--exclude-env', exclude_envs
)
197 # join envs options to original test options
198 TESTS
= (TESTS
+ ' ' + ' '.join(test_options
)).strip()
202 # Allow getting a full CI with
203 # git push -o ci.variable='AUTOBUILD_FAIL_IMMEDIATELY=0'
205 FAIL_IMMEDIATELY
= os
.getenv("AUTOBUILD_FAIL_IMMEDIATELY", "1")
207 if int(FAIL_IMMEDIATELY
):
208 _options
.append('FAIL_IMMEDIATELY=1')
210 _options
.append("TESTS='{}'".format(TESTS
))
212 if INJECT_SELFTEST_PREFIX
:
213 _options
.append("TEST_OPTIONS='--with-selftest-prefix={}'".format("${SELFTEST_PREFIX}"))
214 _options
.append("--directory='{}'".format("${TEST_SOURCE_DIR}"))
216 return ' '.join([cmd
] + _options
)
219 # When updating this list, also update .gitlab-ci.yml to add the job
220 # and to make it a dependency of 'page' for the coverage report.
225 ("random-sleep", random_sleep(300, 900)),
226 ("configure", "./configure " + ctdb_configure_params
),
227 ("make", "make all"),
228 ("install", "make install"),
229 ("test", "make autotest"),
230 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
231 ("clean", "make clean"),
236 ("random-sleep", random_sleep(300, 900)),
237 ("autoconf", "autoconf"),
238 ("configure", "./configure"),
239 ("make", "make html htmlman"),
240 ("publish-docs", PUBLISH_DOCS
),
241 ("clean", "make clean"),
246 "git-clone-required": True,
248 ("configure", "./configure.developer" + samba_configure_params
),
250 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
251 ("chmod-R-a-w", "chmod -R a-w ."),
256 "git-clone-required": True,
258 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
260 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
261 ("chmod-R-a-w", "chmod -R a-w ."),
266 "git-clone-required": True,
268 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json" + samba_configure_params
),
270 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
271 ("chmod-R-a-w", "chmod -R a-w ."),
276 "git-clone-required": True,
278 ("configure", "./configure.developer --without-ad-dc --with-system-heimdalkrb5" + samba_configure_params
),
280 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
281 ("chmod-R-a-w", "chmod -R a-w ."),
285 "samba-without-smb1-build": {
286 "git-clone-required": True,
288 ("configure", "./configure.developer --without-smb1-server --without-ad-dc" + samba_configure_params
),
290 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
291 ("chmod-R-a-w", "chmod -R a-w ."),
295 "samba-no-opath-build": {
296 "git-clone-required": True,
298 ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params
),
300 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
301 ("chmod-R-a-w", "chmod -R a-w ."),
305 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
308 ("random-sleep", random_sleep(300, 900)),
309 ("configure", "./configure.developer" + samba_configure_params
),
311 ("test", make_test(exclude_envs
=[
324 "ad_dc_default_smb1",
332 "ad_member_idmap_rid",
333 "admem_idmap_autorid",
334 "ad_member_idmap_ad",
336 "ad_member_idmap_nss",
343 "fileserver_smb1_done",
357 "ad_dc_default_smb1",
358 "ad_dc_default_smb1_done",
366 ("test-slow-none", make_test(cmd
='make test', TESTS
="--include=selftest/slow-none", include_envs
=["none"])),
368 ("install", "make install"),
369 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
370 ("clean", "make clean"),
374 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
377 ("random-sleep", random_sleep(300, 900)),
378 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
380 ("test", make_test(exclude_envs
=[
393 "ad_dc_default_smb1",
394 "ad_dc_default_smb1_done",
402 "ad_member_idmap_rid",
403 "admem_idmap_autorid",
404 "ad_member_idmap_ad",
406 "ad_member_idmap_nss",
413 "fileserver_smb1_done",
427 "ad_dc_default_smb1",
428 "ad_dc_default_smb1_done",
437 ("install", "make install"),
438 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
439 ("clean", "make clean"),
444 "dependency": "samba-nt4-build",
446 ("random-sleep", random_sleep(300, 900)),
447 ("test", make_test(include_envs
=[
456 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
460 "samba-fileserver": {
461 "dependency": "samba-h5l-build",
463 ("random-sleep", random_sleep(300, 900)),
464 ("test", make_test(include_envs
=[
467 "fileserver_smb1_done",
469 "ktest", # ktest is also tested in samba-ktest-mit samba
470 # and samba-mitkrb5 but is tested here against
474 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
478 "samba-fileserver-without-smb1": {
479 "dependency": "samba-without-smb1-build",
481 ("random-sleep", random_sleep(300, 900)),
482 ("test", make_test(include_envs
=["fileserver"])),
484 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
488 # This is a full build without the AD DC so we test the build with
489 # MIT Kerberos from the current system. Runtime behaviour is
490 # confirmed via the ktest (static ccache and keytab) environment
492 # This environment also used to confirm we can still build with --with-libunwind
495 ("random-sleep", random_sleep(300, 900)),
496 ("configure", "./configure.developer --without-ad-dc --with-libunwind --with-system-mitkrb5 " + samba_configure_params
),
498 ("test", make_test(include_envs
=[
499 "ktest", # ktest is also tested in fileserver, samba and
500 # samba-mitkrb5 but is tested here against a
504 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
509 "dependency": "samba-def-build",
511 ("random-sleep", random_sleep(300, 900)),
512 ("test", make_test(include_envs
=[
514 "ad_member_idmap_rid",
515 "admem_idmap_autorid",
516 "ad_member_idmap_ad",
518 "ad_member_idmap_nss",
519 "ad_member_offlogon",
522 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
527 "dependency": "samba-no-opath-build",
529 ("random-sleep", random_sleep(300, 900)),
531 cmd
="make testonly DISABLE_OPATH=1",
541 ("check-clean-tree", "script/clean-source-tree.sh"),
546 "dependency": "samba-no-opath-build",
548 ("random-sleep", random_sleep(300, 900)),
550 cmd
="make testonly DISABLE_OPATH=1",
554 "fileserver_smb1_done",
557 ("check-clean-tree", "script/clean-source-tree.sh"),
562 "dependency": "samba-def-build",
564 ("random-sleep", random_sleep(1, 1)),
565 ("test", make_test(include_envs
=[
573 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
578 "dependency": "samba-def-build",
580 ("random-sleep", random_sleep(1, 1)),
581 ("test", make_test(include_envs
=[
587 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
592 "dependency": "samba-def-build",
594 ("random-sleep", random_sleep(1, 1)),
595 ("test", make_test(include_envs
=[
602 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
607 "dependency": "samba-def-build",
609 ("random-sleep", random_sleep(1, 1)),
610 ("test", make_test(include_envs
=[
616 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
620 "dependency": "samba-def-build",
622 ("random-sleep", random_sleep(1, 1)),
623 ("test", make_test(include_envs
=[
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
=[
637 "ad_dc_default", "ad_dc_default_smb1", "ad_dc_default_smb1_done"])),
639 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
644 "dependency": "samba-def-build",
646 ("random-sleep", random_sleep(1, 1)),
647 ("test", make_test(include_envs
=["ad_dc_slowtests", "ad_dc_backup"])),
649 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
653 "samba-schemaupgrade": {
654 "dependency": "samba-def-build",
656 ("random-sleep", random_sleep(1, 1)),
657 ("test", make_test(include_envs
=["schema_dc", "schema_pair_dc"])),
659 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
663 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
664 # This is currently the longest task, so we don't randomly delay it.
665 "samba-ad-dc-ntvfs": {
666 "dependency": "samba-def-build",
668 ("random-sleep", random_sleep(1, 1)),
669 ("test", make_test(include_envs
=["ad_dc_ntvfs"])),
671 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
675 # Test fips compliance
677 "dependency": "samba-mit-build",
679 ("random-sleep", random_sleep(1, 1)),
680 ("test", make_test(include_envs
=["ad_dc_fips", "ad_member_fips"])),
681 # TODO: This seems to generate only an empty samba-fips.info ("lcov", LCOV_CMD),
682 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
686 # run the backup/restore testenvs separately as they're fairly standalone
687 # (and CI seems to max out at ~3 different DCs running at once)
689 "dependency": "samba-def-build",
691 ("random-sleep", random_sleep(300, 900)),
692 ("test", make_test(include_envs
=[
698 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
702 "dependency": "samba-def-build",
704 ("random-sleep", random_sleep(300, 900)),
705 ("test", make_test(include_envs
=[
711 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
716 "dependency": "samba-mit-build",
718 ("random-sleep", random_sleep(1, 1)),
719 ("test", make_test(include_envs
=[
721 "ad_member_idmap_rid",
722 "admem_idmap_autorid",
723 "ad_member_idmap_ad",
725 "ad_member_idmap_nss",
726 "ad_member_offlogon",
729 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
733 "samba-addc-mit-1": {
734 "dependency": "samba-mit-build",
736 ("random-sleep", random_sleep(1, 1)),
737 ("test", make_test(include_envs
=[
745 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
749 "samba-addc-mit-4a": {
750 "dependency": "samba-mit-build",
752 ("random-sleep", random_sleep(1, 1)),
753 ("test", make_test(include_envs
=[
759 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
762 "samba-addc-mit-4b": {
763 "dependency": "samba-mit-build",
765 ("random-sleep", random_sleep(1, 1)),
766 ("test", make_test(include_envs
=[
771 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
777 ("configure", "./configure.developer --abi-check-disable" + samba_configure_params
),
779 ("test", make_test(TESTS
="${TESTS}")),
784 # Test cross-compile infrastructure
787 ("random-sleep", random_sleep(900, 1500)),
788 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
789 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
790 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
),
791 ("verify-cross-execute-output", "grep '^Checking value of NSIG' ./bin-xe/cross-answers.txt"),
792 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
793 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
),
794 ("compare-results", "script/compare_cc_results.py "
795 "./bin/c4che/default{} "
796 "./bin-xe/c4che/default{} "
797 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX
]*3))),
798 ("modify-cross-answers", "sed -i.bak -e 's/^\\(Checking value of NSIG:\\) .*/\\1 \"1234\"/' ./bin-xe/cross-answers.txt"),
799 ("configure-cross-answers-modified", "./configure.developer --out ./bin-xa2 --cross-compile" \
800 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa2/ab" + samba_configure_params
),
801 ("verify-cross-answers", "test $(sed -n -e 's/VALUEOF_NSIG = \\(.*\\)/\\1/p' ./bin-xa2/c4che/default{})" \
802 " = \"'1234'\"".format(CACHE_SUFFIX
)),
803 ("invalidate-cross-answers", "sed -i.bak -e '/^Checking value of NSIG/d' ./bin-xe/cross-answers.txt"),
804 ("configure-cross-answers-fail", "./configure.developer --out ./bin-xa3 --cross-compile" \
805 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa3/ab" + samba_configure_params
+ \
810 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
813 ("random-sleep", random_sleep(300, 900)),
814 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --abi-check-disable" + samba_configure_params
),
816 ("test", make_test(cmd
='make test', TESTS
="--exclude=selftest/slow-none", include_envs
=["none"])),
817 ("quicktest", make_test(cmd
='make quicktest', include_envs
=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
819 ("install", "make install"),
820 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
821 ("clean", "make clean"),
827 ("random-sleep", random_sleep(300, 900)),
828 ("configure", "./configure.developer --abi-check-disable --disable-warnings-as-errors" + samba_configure_params
),
830 ("nonetest", make_test(cmd
='make test', TESTS
="--exclude=selftest/slow-none", include_envs
=["none"])),
831 ("quicktest", make_test(cmd
='make quicktest', include_envs
=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
832 ("ktest", make_test(cmd
='make test', include_envs
=["ktest"])),
833 ("install", "make install"),
834 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
835 ("clean", "make clean"),
841 ("random-sleep", random_sleep(900, 1500)),
843 # make sure we have tdb around:
844 ("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}"),
845 ("tdb-make", "cd lib/tdb && make"),
846 ("tdb-install", "cd lib/tdb && make install"),
848 # build samba with cluster support (also building ctdb):
850 "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH "
851 "PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} "
852 "./configure.developer ${PREFIX} "
853 "--with-selftest-prefix=./bin/ab "
854 "--with-cluster-support "
856 "--bundled-libraries=!tdb"),
857 ("samba-make", "make"),
858 ("samba-check", "./bin/smbd --configfile=/dev/null -b | grep CLUSTER_SUPPORT"),
859 ("samba-install", "make install"),
860 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd"),
863 cmd
='PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH make test',
864 INJECT_SELFTEST_PREFIX
=0,
865 include_envs
=["clusteredmember"])
869 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
870 ("clean", "make clean"),
871 ("ctdb-clean", "cd ./ctdb && make clean"),
877 ("random-sleep", random_sleep(300, 900)),
878 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
),
879 ("talloc-make", "cd lib/talloc && make"),
880 ("talloc-install", "cd lib/talloc && make install"),
882 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
),
883 ("tdb-make", "cd lib/tdb && make"),
884 ("tdb-install", "cd lib/tdb && make install"),
886 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
),
887 ("tevent-make", "cd lib/tevent && make"),
888 ("tevent-install", "cd lib/tevent && make install"),
890 ("nondevel-configure", samba_libs_envvars
+ " ./configure --private-libraries='!ldb' ${PREFIX}"),
891 ("nondevel-make", "make -j"),
892 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"),
893 ("nondevel-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
894 ("nondevel-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
895 ("nondevel-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
896 ("nondevel-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
897 ("nondevel-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
898 ("nondevel-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
899 ("nondevel-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
900 ("nondevel-no-public-nss_winbind",
901 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
902 ("nondevel-no-public-nss_wins",
903 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
904 ("nondevel-no-public-libwbclient",
905 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
906 ("nondevel-no-public-pam_winbind",
907 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
908 ("nondevel-no-public-winbind_krb5_locator",
909 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
910 ("nondevel-no-public-async_dns_krb5_locator",
911 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
912 ("nondevel-install", "make -j install"),
913 ("nondevel-dist", "make dist"),
915 ("prefix-no-private-libtalloc", "find ${PREFIX_DIR} | grep -v 'libtalloc-report' | grep 'private.*libtalloc' && exit 1; exit 0"),
916 ("prefix-no-private-libtdb", "find ${PREFIX_DIR} | grep -v 'libtdb-wrap' | grep 'private.*libtdb' && exit 1; exit 0"),
917 ("prefix-no-private-libtevent", "find ${PREFIX_DIR} | grep -v 'libtevent-util' | grep 'private.*libtevent' && exit 1; exit 0"),
918 ("prefix-no-private-libldb", "find ${PREFIX_DIR} | grep -v 'module' | grep -v 'libldbsamba' | grep 'private.*libldb.so' && exit 1; exit 0"),
919 ("prefix-public-libldb", "find ${PREFIX_DIR} | grep 'lib/libldb.so' && exit 0; exit 1"),
920 ("prefix-no-samba-nss_winbind", "ldd ${PREFIX_DIR}/lib/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
921 ("prefix-no-samba-nss_wins", "ldd ${PREFIX_DIR}/lib/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
922 ("prefix-no-samba-libwbclient", "ldd ${PREFIX_DIR}/lib/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
923 ("prefix-no-samba-pam_winbind", "ldd ${PREFIX_DIR}/lib/security/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
924 ("prefix-no-public-nss_winbind",
925 check_symbols("${PREFIX_DIR}/lib/libnss_winbind.so.2", "_nss_winbind_")),
926 ("prefix-no-public-nss_wins",
927 check_symbols("${PREFIX_DIR}/lib/libnss_wins.so.2", "_nss_wins_")),
928 ("prefix-no-public-libwbclient",
929 check_symbols("${PREFIX_DIR}/lib/libwbclient.so.0", "wbc")),
930 ("prefix-no-public-pam_winbind",
931 check_symbols("${PREFIX_DIR}/lib/security/pam_winbind.so", "pam_sm_")),
932 ("prefix-no-public-winbind_krb5_locator",
933 check_symbols("${PREFIX_DIR}/lib/krb5/winbind_krb5_locator.so",
935 ("prefix-no-public-async_dns_krb5_locator",
936 check_symbols("${PREFIX_DIR}/lib/krb5/async_dns_krb5_locator.so",
939 # retry with all modules shared
940 ("allshared-distclean", "make distclean"),
941 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL"),
942 ("allshared-make", "make -j"),
943 ("allshared-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
944 ("allshared-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
945 ("allshared-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
946 ("allshared-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
947 ("allshared-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
948 ("allshared-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
949 ("allshared-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
950 ("allshared-no-public-nss_winbind",
951 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
952 ("allshared-no-public-nss_wins",
953 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
954 ("allshared-no-public-libwbclient",
955 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
956 ("allshared-no-public-pam_winbind",
957 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
958 ("allshared-no-public-winbind_krb5_locator",
959 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
960 ("allshared-no-public-async_dns_krb5_locator",
961 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
967 # build the fuzzers (static) via the oss-fuzz script
968 ("fuzzers-mkdir-prefix", "mkdir -p ${PREFIX_DIR}"),
969 ("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"),
973 # * Test smbd and smbtorture can build semi-static
975 # * Test Samba without python still builds.
977 # When this test fails due to more use of Python, the expectations
978 # is that the newly failing part of the code should be disabled
979 # when --disable-python is set (rather than major work being done
980 # to support this environment).
982 # The target here is for vendors shipping a minimal smbd.
983 "samba-minimal-smbd": {
985 ("random-sleep", random_sleep(300, 900)),
987 # build with all modules static
988 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL"),
989 ("allstatic-make", "make -j"),
990 ("allstatic-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
991 ("allstatic-lcov", LCOV_CMD
),
992 ("allstatic-def-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
993 ("allstatic-def-clean", "make clean"),
995 # force all libraries as private
996 ("allprivate-def-distclean", "make distclean"),
997 ("allprivate-def-configure", "./configure.developer " + samba_configure_params
+ " --private-libraries=ALL"),
998 ("allprivate-def-make", "make -j"),
999 # note wrapper libraries need to be public
1000 ("allprivate-def-no-public", "ls ./bin/shared | egrep -v '^private$|lib[nprsu][saeoi][smscd].*-wrapper.so$|pam_set_items.so' | wc -l | grep -q '^0'"),
1001 ("allprivate-def-only-private-ext", "ls ./bin/shared/private | egrep 'private-samba' | wc -l | grep -q '^0' && exit 1; exit 0"),
1002 ("allprivate-def-no-non-private-ext", "ls ./bin/shared/private | egrep -v 'private-samba|^libpypamtest.so$' | wc -l | grep -q '^0'"),
1003 ("allprivate-def-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
1004 ("allprivate-def-lcov", LCOV_CMD
),
1005 ("allprivate-def-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1006 ("allprivate-def-clean", "make clean"),
1008 # force all libraries as private with a non default
1009 # extension and 2 exceptions
1010 ("allprivate-ext-distclean", "make distclean"),
1011 ("allprivate-ext-configure", "./configure.developer " + samba_configure_params
+ " --private-libraries=ALL --private-library-extension=private-library --private-extension-exception=pac,ndr"),
1012 ("allprivate-ext-make", "make -j"),
1013 # note wrapper libraries need to be public
1014 ("allprivate-ext-no-public", "ls ./bin/shared | egrep -v '^private$|lib[nprsu][saeoi][smscd].*-wrapper.so$|pam_set_items.so' | wc -l | grep -q '^0'"),
1015 ("allprivate-ext-no-private-default-ext", "ls ./bin/shared/private | grep 'private-samba' | wc -l | grep -q '^0'"),
1016 ("allprivate-ext-has-private-ext", "ls ./bin/shared/private | grep 'private-library' | wc -l | grep -q '^0' && exit 1; exit 0"),
1017 ("allprivate-ext-libndr-no-private-ext", "ls ./bin/shared/private | grep -v 'private-library' | grep 'libndr' | wc -l | grep -q '^1'"),
1018 ("allprivate-ext-libpac-no-private-ext", "ls ./bin/shared/private | grep -v 'private-library' | grep 'libpac' | wc -l | grep -q '^1'"),
1019 ("allprivate-ext-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
1020 ("allprivate-ext-lcov", LCOV_CMD
),
1021 ("allprivate-ext-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1022 ("allprivate-ext-clean", "make clean"),
1024 # retry with nonshared smbd and smbtorture
1025 ("nonshared-distclean", "make distclean"),
1026 ("nonshared-configure", "./configure.developer " + samba_configure_params
+ " --bundled-libraries=ALL --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd"),
1027 ("nonshared-make", "make -j"),
1028 ("nonshared-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
1029 ("nonshared-lcov", LCOV_CMD
),
1030 ("nonshared-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1031 ("nonshared-clean", "make clean"),
1037 ("random-sleep", random_sleep(300, 900)),
1039 ("configure", "./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
1040 ("make", "make -j"),
1041 ("find-python", "script/find_python.sh ${PREFIX}"),
1042 ("test", "make test-nopython"),
1044 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1045 ("clean", "make clean"),
1047 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
1048 ("talloc-make", "cd lib/talloc && make"),
1049 ("talloc-install", "cd lib/talloc && make install"),
1051 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
1052 ("tdb-make", "cd lib/tdb && make"),
1053 ("tdb-install", "cd lib/tdb && make install"),
1055 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
1056 ("tevent-make", "cd lib/tevent && make"),
1057 ("tevent-install", "cd lib/tevent && make install"),
1059 # retry against installed library packages, but no required modules
1060 ("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"),
1061 ("libs-make", "make -j"),
1062 ("libs-install", "make install"),
1063 ("libs-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1064 ("libs-clean", "make clean"),
1069 "samba-codecheck": {
1071 ("run", "script/check-shell-scripts.sh ."),
1072 ("run", "script/codespell.sh ."),
1078 ("random-sleep", random_sleep(60, 600)),
1079 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1081 ("install", "make install"),
1082 ("test", "make test"),
1084 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1085 ("distcheck", "make distcheck"),
1086 ("clean", "make clean"),
1092 ("random-sleep", random_sleep(60, 600)),
1093 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1095 ("install", "make install"),
1096 ("test", "make test"),
1098 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1099 ("distcheck", "make distcheck"),
1100 ("clean", "make clean"),
1106 ("random-sleep", random_sleep(60, 600)),
1107 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1109 ("install", "make install"),
1110 ("test", "make test"),
1112 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1113 ("distcheck", "make distcheck"),
1114 ("clean", "make clean"),
1120 ("random-sleep", random_sleep(60, 600)),
1121 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1123 ("install", "make install"),
1124 ("test", "make test"),
1126 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1127 ("distcheck", "make distcheck"),
1128 ("clean", "make clean"),
1133 "git-clone-required": True,
1135 ("random-sleep", random_sleep(60, 600)),
1136 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}"),
1137 ("touch", "touch *.yp"),
1139 ("test", "make test"),
1140 ("install", "make install"),
1141 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm"),
1142 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1143 ("clean", "make clean"),
1147 # these are useful for debugging autobuild
1150 ("pass", 'echo passing && /bin/true'),
1155 ("fail", 'echo failing && /bin/false'),
1160 defaulttasks
= list(tasks
.keys())
1162 defaulttasks
.remove("pass")
1163 defaulttasks
.remove("fail")
1165 # The build tasks will be brought in by the test tasks as needed
1166 defaulttasks
.remove("samba-def-build")
1167 defaulttasks
.remove("samba-nt4-build")
1168 defaulttasks
.remove("samba-mit-build")
1169 defaulttasks
.remove("samba-h5l-build")
1170 defaulttasks
.remove("samba-no-opath-build")
1172 # This is not a normal test, but a task to support manually running
1173 # one test under autobuild
1174 defaulttasks
.remove("samba-test-only")
1176 # Only built on GitLab CI and not in the default autobuild because it
1177 # uses too much space (4GB of semi-static binaries)
1178 defaulttasks
.remove("samba-fuzz")
1180 # The FIPS build runs only in GitLab CI on a current Fedora Docker
1181 # container where a simulated FIPS mode is possible.
1182 defaulttasks
.remove("samba-fips")
1184 # The MIT build runs on a current Fedora where an up to date MIT KDC
1185 # is already packaged. This avoids needing to backport a current MIT
1186 # to the default Ubuntu 18.04, particularly during development, and
1187 # the need to install on the shared sn-devel-184.
1189 defaulttasks
.remove("samba-mitkrb5")
1190 defaulttasks
.remove("samba-admem-mit")
1191 defaulttasks
.remove("samba-addc-mit-1")
1192 defaulttasks
.remove("samba-addc-mit-4a")
1193 defaulttasks
.remove("samba-addc-mit-4b")
1195 defaulttasks
.remove("samba-32bit")
1197 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
1198 defaulttasks
.remove("samba-o3")
1212 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
1214 do_debug("Running: '%s' in '%s'" % (cmd
, dir))
1216 do_print("Running: '%s' in '%s'" % (cmd
, dir))
1219 out
= check_output([cmd
], shell
=True, cwd
=dir)
1220 return out
.decode(encoding
='utf-8', errors
='backslashreplace')
1222 return check_call(cmd
, shell
=True, cwd
=dir)
1224 return call(cmd
, shell
=True, cwd
=dir)
1226 def rmdir_force(dirname
, re_raise
=True):
1228 run_cmd("test -d %s && chmod -R +w %s; rm -rf %s" % (
1229 dirname
, dirname
, dirname
), output
=True, show
=True)
1230 except CalledProcessError
as e
:
1231 do_print("Failed: '%s'" % (str(e
)))
1232 run_cmd("tree %s" % dirname
, output
=True, show
=True)
1238 class builder(object):
1239 '''handle build of one directory'''
1241 def __init__(self
, name
, definition
):
1243 self
.dir = builddirs
.get(name
, '.')
1244 self
.tag
= self
.name
.replace('/', '_')
1245 self
.definition
= definition
1246 self
.sequence
= definition
["sequence"]
1247 self
.git_clone_required
= False
1248 if "git-clone-required" in definition
:
1249 self
.git_clone_required
= bool(definition
["git-clone-required"])
1253 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
1254 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
1255 do_debug("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
1256 do_debug("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
1257 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
1258 self
.stdout
= open(self
.stdout_path
, 'w')
1259 self
.stderr
= open(self
.stderr_path
, 'w')
1260 self
.stdin
= open("/dev/null", 'r')
1261 self
.builder_dir
= "%s/%s" % (testbase
, self
.tag
)
1262 self
.test_source_dir
= self
.builder_dir
1263 self
.cwd
= "%s/%s" % (self
.builder_dir
, self
.dir)
1264 self
.selftest_prefix
= "%s/bin/ab" % (self
.cwd
)
1265 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
1267 self
.producer
= None
1269 if self
.git_clone_required
:
1270 assert "dependency" not in definition
1272 def mark_existing(self
):
1273 do_debug('%s: Mark as existing dependency' % self
.name
)
1274 self
.next
= len(self
.sequence
)
1277 def add_consumer(self
, consumer
):
1278 do_debug("%s: add consumer: %s" % (self
.name
, consumer
.name
))
1279 consumer
.producer
= self
1280 consumer
.test_source_dir
= self
.test_source_dir
1281 self
.consumers
.append(consumer
)
1283 def start_next(self
):
1284 if self
.producer
is not None:
1285 if not self
.producer
.done
:
1286 do_debug("%s: Waiting for producer: %s" % (self
.name
, self
.producer
.name
))
1290 rmdir_force(self
.builder_dir
)
1291 rmdir_force(self
.prefix
)
1292 if self
.producer
is not None:
1293 run_cmd("mkdir %s" % (self
.builder_dir
), dir=test_master
, show
=True)
1294 elif not self
.git_clone_required
:
1295 run_cmd("cp -R -a -l %s %s" % (test_master
, self
.builder_dir
), dir=test_master
, show
=True)
1297 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.builder_dir
), dir=test_master
, show
=True)
1299 if self
.next
== len(self
.sequence
):
1301 do_print('%s: Completed OK' % self
.name
)
1303 if not options
.nocleanup
and len(self
.consumers
) == 0:
1304 do_print('%s: Cleaning up' % self
.name
)
1305 rmdir_force(self
.builder_dir
)
1306 rmdir_force(self
.prefix
)
1307 for consumer
in self
.consumers
:
1308 if consumer
.next
!= 0:
1310 do_print('%s: Starting consumer %s' % (self
.name
, consumer
.name
))
1311 consumer
.start_next()
1312 if self
.producer
is not None:
1313 self
.producer
.consumers
.remove(self
)
1314 assert self
.producer
.done
1315 self
.producer
.start_next()
1316 do_print('%s: Remaining consumers %u' % (self
.name
, len(self
.consumers
)))
1318 (self
.stage
, self
.cmd
) = self
.sequence
[self
.next
]
1319 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}",
1320 get_path(name
='platlib',
1321 scheme
="posix_prefix",
1322 vars={"base": self
.prefix
,
1323 "platbase": self
.prefix
}))
1324 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
1325 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
1326 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
1327 self
.cmd
= self
.cmd
.replace("${TEST_SOURCE_DIR}", self
.test_source_dir
)
1328 self
.cmd
= self
.cmd
.replace("${SELFTEST_PREFIX}", self
.selftest_prefix
)
1329 self
.cmd
= self
.cmd
.replace("${LOG_BASE}", options
.log_base
)
1330 self
.cmd
= self
.cmd
.replace("${NAME}", self
.name
)
1331 self
.cmd
= self
.cmd
.replace("${ENABLE_COVERAGE}", options
.enable_coverage
)
1332 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, self
.cwd
))
1333 self
.proc
= Popen(self
.cmd
, shell
=True,
1334 close_fds
=True, cwd
=self
.cwd
,
1335 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
1338 def expand_dependencies(n
):
1340 if "dependency" in tasks
[n
]:
1341 depname
= tasks
[n
]["dependency"]
1342 assert depname
in tasks
1343 sdeps
= expand_dependencies(depname
)
1344 assert n
not in sdeps
1347 deps
.append(depname
)
1351 class buildlist(object):
1352 '''handle build of multiple directories'''
1354 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
1355 self
.tail_proc
= None
1358 if options
.restrict_tests
:
1359 tasknames
= ["samba-test-only"]
1361 tasknames
= defaulttasks
1363 given_tasknames
= tasknames
.copy()
1364 implicit_tasknames
= []
1365 for n
in given_tasknames
:
1366 deps
= expand_dependencies(n
)
1368 if dep
in given_tasknames
:
1370 if dep
in implicit_tasknames
:
1372 implicit_tasknames
.append(dep
)
1374 tasknames
= implicit_tasknames
.copy()
1375 tasknames
.extend(given_tasknames
)
1376 do_debug("given_tasknames: %s" % given_tasknames
)
1377 do_debug("implicit_tasknames: %s" % implicit_tasknames
)
1378 do_debug("tasknames: %s" % tasknames
)
1379 self
.tlist
= [builder(n
, tasks
[n
]) for n
in tasknames
]
1382 rebase_remote
= "rebaseon"
1384 "git-clone-required": True,
1388 git remote add -t %s %s %s
1392 git describe %s/%s > old_remote_branch.desc
1394 git describe %s/%s > remote_branch.desc
1395 diff old_remote_branch.desc remote_branch.desc
1398 rebase_branch
, rebase_remote
, rebase_url
,
1400 rebase_remote
, rebase_branch
,
1402 rebase_remote
, rebase_branch
1405 self
.retry
= builder('retry', retry_task
)
1406 self
.need_retry
= False
1408 if options
.skip_dependencies
:
1409 for b
in self
.tlist
:
1410 if b
.name
in implicit_tasknames
:
1413 for b
in self
.tlist
:
1414 do_debug("b.name=%s" % b
.name
)
1415 if "dependency" not in b
.definition
:
1417 depname
= b
.definition
["dependency"]
1418 do_debug("b.name=%s: dependency:%s" % (b
.name
, depname
))
1419 for p
in self
.tlist
:
1420 if p
.name
== depname
:
1423 def kill_kids(self
):
1424 if self
.tail_proc
is not None:
1425 self
.tail_proc
.terminate()
1426 self
.tail_proc
.wait()
1427 self
.tail_proc
= None
1428 if self
.retry
is not None:
1429 self
.retry
.proc
.terminate()
1430 self
.retry
.proc
.wait()
1432 for b
in self
.tlist
:
1433 if b
.proc
is not None:
1434 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.test_source_dir
, checkfail
=False)
1442 for b
in self
.tlist
:
1445 none_running
= False
1446 b
.status
= b
.proc
.poll()
1447 if b
.status
is None:
1452 ret
= self
.retry
.proc
.poll()
1454 self
.need_retry
= True
1462 for b
in self
.tlist
:
1465 self
.retry
.start_next()
1468 if options
.retry
and self
.need_retry
:
1470 do_print("retry needed")
1471 return (0, None, None, None, "retry")
1474 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
1476 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
1479 return (0, None, None, None, "All OK")
1481 def write_system_info(self
, filename
):
1482 with
open(filename
, 'w') as f
:
1483 for cmd
in ['uname -a',
1487 'cat /proc/cpuinfo',
1490 'df -m %s' % testbase
]:
1492 out
= run_cmd(cmd
, output
=True, checkfail
=False)
1493 except CalledProcessError
as e
:
1494 out
= "<failed: %s>" % str(e
)
1495 print('### %s' % cmd
, file=f
)
1499 def tarlogs(self
, fname
):
1500 with tarfile
.open(fname
, "w:gz") as tar
:
1501 for b
in self
.tlist
:
1502 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
1503 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
1504 if os
.path
.exists("autobuild.log"):
1505 tar
.add("autobuild.log")
1506 filename
= 'system-info.txt'
1507 self
.write_system_info(filename
)
1510 def remove_logs(self
):
1511 for b
in self
.tlist
:
1512 os
.unlink(b
.stdout_path
)
1513 os
.unlink(b
.stderr_path
)
1515 def start_tail(self
):
1516 cmd
= ["tail", "-f"]
1517 for b
in self
.tlist
:
1518 cmd
.append(b
.stdout_path
)
1519 cmd
.append(b
.stderr_path
)
1520 self
.tail_proc
= Popen(cmd
, close_fds
=True)
1523 def cleanup(do_raise
=False):
1524 if options
.nocleanup
:
1526 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
1527 run_cmd("stat %s" % testbase
, show
=True)
1528 do_print("Cleaning up %r" % cleanup_list
)
1529 for d
in cleanup_list
:
1530 ok
= rmdir_force(d
, re_raise
=False)
1533 if os
.path
.isdir(d
):
1534 do_print("Killing, waiting and retry")
1535 run_cmd("killbysubdir %s > /dev/null 2>&1" % d
, checkfail
=False)
1537 do_print("Waiting and retry")
1539 rmdir_force(d
, re_raise
=do_raise
)
1542 def daemonize(logfile
):
1544 if pid
== 0: # Parent
1547 if pid
!= 0: # Actual daemon
1552 import resource
# Resource usage information.
1553 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
1554 if maxfd
== resource
.RLIM_INFINITY
:
1555 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
1556 for fd
in range(0, maxfd
):
1561 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
1566 def write_pidfile(fname
):
1567 '''write a pid file, cleanup on exit'''
1568 with
open(fname
, mode
='w') as f
:
1569 f
.write("%u\n" % os
.getpid())
1572 def rebase_tree(rebase_url
, rebase_branch
="master"):
1573 rebase_remote
= "rebaseon"
1574 do_print("Rebasing on %s" % rebase_url
)
1575 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1576 run_cmd("git remote add -t %s %s %s" %
1577 (rebase_branch
, rebase_remote
, rebase_url
),
1578 show
=True, dir=test_master
)
1579 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
1580 if options
.fix_whitespace
:
1581 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
1582 (rebase_remote
, rebase_branch
),
1583 show
=True, dir=test_master
)
1585 run_cmd("git rebase --force-rebase %s/%s" %
1586 (rebase_remote
, rebase_branch
),
1587 show
=True, dir=test_master
)
1588 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
1589 (rebase_remote
, rebase_branch
),
1590 dir=test_master
, output
=True)
1592 do_print("No differences between HEAD and %s/%s - exiting" %
1593 (rebase_remote
, rebase_branch
))
1595 run_cmd("git describe %s/%s" %
1596 (rebase_remote
, rebase_branch
),
1597 show
=True, dir=test_master
)
1598 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1599 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
1600 (rebase_remote
, rebase_branch
),
1601 show
=True, dir=test_master
)
1604 def push_to(push_url
, push_branch
="master"):
1605 push_remote
= "pushto"
1606 do_print("Pushing to %s" % push_url
)
1608 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
1609 run_cmd("git commit --amend -c HEAD", dir=test_master
)
1610 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
1611 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
1612 run_cmd("git remote add -t %s %s %s" %
1613 (push_branch
, push_remote
, push_url
),
1614 show
=True, dir=test_master
)
1615 run_cmd("git push %s +HEAD:%s" %
1616 (push_remote
, push_branch
),
1617 show
=True, dir=test_master
)
1620 def send_email(subject
, text
, log_tar
):
1621 if options
.email
is None:
1622 do_print("not sending email because the recipient is not set")
1623 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
1626 outer
= MIMEMultipart()
1627 outer
['Subject'] = subject
1628 outer
['To'] = options
.email
1629 outer
['From'] = options
.email_from
1630 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
1631 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
1632 outer
.attach(MIMEText(text
, 'plain', 'utf-8'))
1633 if options
.attach_logs
:
1634 with
open(log_tar
, 'rb') as fp
:
1635 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
1636 # Set the filename parameter
1637 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
1639 content
= outer
.as_string()
1640 s
= smtplib
.SMTP(options
.email_server
)
1641 email_user
= os
.getenv('SMTP_USERNAME')
1642 email_password
= os
.getenv('SMTP_PASSWORD')
1643 if email_user
is not None:
1645 s
.login(email_user
, email_password
)
1647 s
.sendmail(options
.email_from
, [options
.email
], content
)
1652 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1653 elapsed_time
, log_base
=None, add_log_tail
=True):
1654 '''send an email to options.email about the failure'''
1655 elapsed_minutes
= elapsed_time
/ 60.0
1656 if log_base
is None:
1661 Your autobuild on %s failed after %.1f minutes
1662 when trying to test %s with the following error:
1666 the autobuild has been abandoned. Please fix the error and resubmit.
1668 A summary of the autobuild process is here:
1671 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
1673 if options
.restrict_tests
:
1675 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1677 if failed_task
!= 'rebase':
1679 You can see logs of the failed task here:
1684 or you can get full logs of all tasks in this job here:
1688 The top commit for the tree that was built was:
1692 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
1694 log_stdout
= "%s/%s.stdout" % (gitroot
, failed_tag
)
1695 if add_log_tail
and os
.access(log_stdout
, os
.R_OK
):
1696 f
= open(log_stdout
, 'r')
1697 lines
= f
.readlines()
1698 log_tail
= "".join(lines
[-50:])
1699 num_lines
= len(lines
)
1700 log_stderr
= "%s/%s.stderr" % (gitroot
, failed_tag
)
1701 if num_lines
< 50 and os
.access(log_stderr
, os
.R_OK
):
1702 # Also include stderr (compile failures) if < 50 lines of stdout
1703 f
= open(log_stderr
, 'r')
1704 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
1707 The last 50 lines of log messages:
1713 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1714 send_email('autobuild[%s] failure on %s for task %s during %s'
1715 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
1719 def email_success(elapsed_time
, log_base
=None):
1720 '''send an email to options.email about a successful build'''
1721 if log_base
is None:
1726 Your autobuild on %s has succeeded after %.1f minutes.
1728 ''' % (platform
.node(), elapsed_time
/ 60.)
1730 if options
.restrict_tests
:
1732 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1734 if options
.keeplogs
:
1737 you can get full logs of all tasks in this job here:
1744 The top commit for the tree that was built was:
1747 ''' % top_commit_msg
1749 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1750 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
1754 # get the top commit message, for emails
1755 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
1758 if options
.skip_dependencies
:
1759 run_cmd("stat %s" % testbase
, dir=testbase
, output
=True)
1761 os
.makedirs(testbase
)
1762 except Exception as reason
:
1763 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
1764 cleanup_list
.append(testbase
)
1767 logfile
= os
.path
.join(testbase
, "log")
1768 do_print("Forking into the background, writing progress to %s" % logfile
)
1771 write_pidfile(gitroot
+ "/autobuild.pid")
1773 start_time
= time
.time()
1777 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
1778 os
.makedirs(test_tmpdir
)
1779 # The waf uninstall code removes empty directories all the way
1780 # up the tree. Creating a file in test_tmpdir stops it from
1782 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
1783 ".directory-is-not-empty"), show
=True)
1784 run_cmd("stat %s" % test_tmpdir
, show
=True)
1785 run_cmd("stat %s" % testbase
, show
=True)
1786 if options
.skip_dependencies
:
1787 run_cmd("stat %s" % test_master
, dir=testbase
, output
=True)
1789 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
1795 if options
.rebase
is not None:
1796 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
1798 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1800 elapsed_time
= time
.time() - start_time
1801 email_failure(-1, 'rebase', 'rebase', 'rebase',
1802 'rebase on %s failed' % options
.branch
,
1803 elapsed_time
, log_base
=options
.log_base
)
1807 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
1810 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
1811 if status
!= 0 or errstr
!= "retry":
1813 cleanup(do_raise
=True)
1818 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1824 do_print("waiting for tail to flush")
1827 elapsed_time
= time
.time() - start_time
1829 if options
.passcmd
is not None:
1830 do_print("Running passcmd: %s" % options
.passcmd
)
1831 run_cmd(options
.passcmd
, dir=test_master
)
1832 if options
.pushto
is not None:
1833 push_to(options
.pushto
, push_branch
=options
.branch
)
1834 if options
.keeplogs
or options
.attach_logs
:
1835 blist
.tarlogs("logs.tar.gz")
1836 do_print("Logs in logs.tar.gz")
1837 if options
.always_email
:
1838 email_success(elapsed_time
, log_base
=options
.log_base
)
1844 # something failed, gather a tar of the logs
1845 blist
.tarlogs("logs.tar.gz")
1847 if options
.email
is not None:
1848 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1849 elapsed_time
, log_base
=options
.log_base
)
1851 elapsed_minutes
= elapsed_time
/ 60.0
1854 ####################################################################
1858 Your autobuild[%s] on %s failed after %.1f minutes
1859 when trying to test %s with the following error:
1863 the autobuild has been abandoned. Please fix the error and resubmit.
1865 ####################################################################
1867 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1871 do_print("Logs in logs.tar.gz")