2 # run tests on all Samba subprojects and push to a git tree on success
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
6 from subprocess
import call
, check_call
, check_output
, Popen
, PIPE
, CalledProcessError
12 from optparse
import OptionParser
15 from email
.mime
.text
import MIMEText
16 from email
.mime
.base
import MIMEBase
17 from email
.mime
.application
import MIMEApplication
18 from email
.mime
.multipart
import MIMEMultipart
19 from distutils
.sysconfig
import get_python_lib
23 from waflib
.Build
import CACHE_SUFFIX
25 sys
.path
.insert(0, "./third_party/waf")
26 from waflib
.Build
import CACHE_SUFFIX
29 os
.environ
["PYTHONUNBUFFERED"] = "1"
31 # This speeds up testing remarkably.
32 os
.environ
['TDB_NO_FSYNC'] = '1'
36 '''get to the top of the git repo'''
39 if os
.path
.exists(os
.path
.join(p
, ".git")):
41 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
45 gitroot
= find_git_root()
47 raise Exception("Failed to find git root")
50 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
52 parser
= OptionParser()
53 parser
.add_option("--tail", help="show output while running", default
=False, action
="store_true")
54 parser
.add_option("--keeplogs", help="keep logs", default
=False, action
="store_true")
55 parser
.add_option("--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
56 parser
.add_option("--skip-dependencies", help="skip to run task dependency tasks", 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("--full-testbase", help="full base directory to run tests in (default %s/b$PID)" % def_testbase
,
61 parser
.add_option("--passcmd", help="command to run on success", default
=None)
62 parser
.add_option("--verbose", help="show all commands as they are run",
63 default
=False, action
="store_true")
64 parser
.add_option("--rebase", help="rebase on the given tree before testing",
65 default
=None, type='str')
66 parser
.add_option("--pushto", help="push to a git url on success",
67 default
=None, type='str')
68 parser
.add_option("--mark", help="add a Tested-By signoff before pushing",
69 default
=False, action
="store_true")
70 parser
.add_option("--fix-whitespace", help="fix whitespace on rebase",
71 default
=False, action
="store_true")
72 parser
.add_option("--retry", help="automatically retry if master changes",
73 default
=False, action
="store_true")
74 parser
.add_option("--email", help="send email to the given address on failure",
75 type='str', default
=None)
76 parser
.add_option("--email-from", help="send email from the given address",
77 type='str', default
="autobuild@samba.org")
78 parser
.add_option("--email-server", help="send email via the given server",
79 type='str', default
='localhost')
80 parser
.add_option("--always-email", help="always send email, even on success",
82 parser
.add_option("--daemon", help="daemonize after initial setup",
84 parser
.add_option("--branch", help="the branch to work on (default=master)",
85 default
="master", type='str')
86 parser
.add_option("--log-base", help="location where the logs can be found (default=cwd)",
87 default
=gitroot
, type='str')
88 parser
.add_option("--attach-logs", help="Attach logs to mails sent on success/failure?",
89 default
=False, action
="store_true")
90 parser
.add_option("--restrict-tests", help="run as make test with this TESTS= regex",
92 parser
.add_option("--enable-coverage", dest
='enable_coverage',
93 action
="store_const", const
='--enable-coverage', default
='',
94 help="Add --enable-coverage option while configure")
96 (options
, args
) = parser
.parse_args()
99 if options
.rebase
is None:
100 raise Exception('You can only use --retry if you also rebase')
102 if options
.full_testbase
is not None:
103 testbase
= options
.full_testbase
105 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
106 test_master
= "%s/master" % testbase
107 test_prefix
= "%s/prefix" % testbase
108 test_tmpdir
= "%s/tmp" % testbase
109 os
.environ
['TMPDIR'] = test_tmpdir
111 if options
.enable_coverage
:
112 LCOV_CMD
= "cd ${TEST_SOURCE_DIR} && lcov --capture --directory . --output-file ${LOG_BASE}/${NAME}.info --rc 'geninfo_adjust_src_path=${TEST_SOURCE_DIR}/'"
114 LCOV_CMD
= 'echo "lcov skipped since no --enable-coverage specified"'
116 if options
.enable_coverage
:
117 PUBLISH_DOCS
= "mkdir -p ${LOG_BASE}/public && mv output/htmldocs ${LOG_BASE}/public/htmldocs"
119 PUBLISH_DOCS
= 'echo "HTML documentation publishing skipped since no --enable-coverage specified"'
121 CLEAN_SOURCE_TREE_CMD
= "cd ${TEST_SOURCE_DIR} && script/clean-source-tree.sh"
124 def check_symbols(sofile
, expected_symbols
=""):
125 return "objdump --dynamic-syms " + sofile
+ " | " + \
126 "awk \'$0 !~ /" + expected_symbols
+ "/ {if ($2 == \"g\" && $3 ~ /D(F|O)/ && $4 ~ /(.bss|.text)/ && $7 !~ /(__gcov_|mangle_path)/) exit 1}\'"
129 # If we are only running specific test,
130 # do not sleep randomly to wait for it to start
131 def random_sleep(low
, high
):
134 def random_sleep(low
, high
):
135 return 'sleep {}'.format(random
.randint(low
, high
))
143 "talloc": "lib/talloc",
144 "replace": "lib/replace",
145 "tevent": "lib/tevent",
147 "docs-xml": "docs-xml"
150 ctdb_configure_params
= " --enable-developer ${PREFIX}"
151 samba_configure_params
= " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data"
153 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
154 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
155 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
156 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check ${ENABLE_COVERAGE} --enable-debug -C ${PREFIX}"
157 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,popt,NONE"
158 samba_libs_configure_bundled_libs
= " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
159 samba_libs_configure_samba
= samba_libs_configure_base
+ samba_libs_configure_bundled_libs
162 def format_option(name
, value
=None):
163 """Format option as str list."""
164 if value
is None: # boolean option
166 if not isinstance(value
, list): # single value option
169 return ['{}={}'.format(name
, item
) for item
in value
]
174 INJECT_SELFTEST_PREFIX
=1,
181 test_options
= format_option('--include-env', include_envs
)
183 test_options
= format_option('--exclude-env', exclude_envs
)
185 # join envs options to original test options
186 TESTS
= (TESTS
+ ' ' + ' '.join(test_options
)).strip()
190 # Allow getting a full CI with
191 # git push -o ci.variable='AUTOBUILD_FAIL_IMMEDIATELY=0'
193 FAIL_IMMEDIATELY
= os
.getenv("AUTOBUILD_FAIL_IMMEDIATELY", "1")
195 if int(FAIL_IMMEDIATELY
):
196 _options
.append('FAIL_IMMEDIATELY=1')
198 _options
.append("TESTS='{}'".format(TESTS
))
200 if INJECT_SELFTEST_PREFIX
:
201 _options
.append("TEST_OPTIONS='--with-selftest-prefix={}'".format("${SELFTEST_PREFIX}"))
202 _options
.append("--directory='{}'".format("${TEST_SOURCE_DIR}"))
204 return ' '.join([cmd
] + _options
)
207 # When updating this list, also update .gitlab-ci.yml to add the job
208 # and to make it a dependency of 'page' for the coverage report.
213 ("random-sleep", random_sleep(300, 900)),
214 ("configure", "./configure " + ctdb_configure_params
),
215 ("make", "make all"),
216 ("install", "make install"),
217 ("test", "make autotest"),
218 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
219 ("clean", "make clean"),
224 ("random-sleep", random_sleep(300, 900)),
225 ("autoconf", "autoconf"),
226 ("configure", "./configure"),
227 ("make", "make html htmlman"),
228 ("publish-docs", PUBLISH_DOCS
),
229 ("clean", "make clean"),
234 "git-clone-required": True,
236 ("configure", "./configure.developer" + samba_configure_params
),
238 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
239 ("chmod-R-a-w", "chmod -R a-w ."),
244 "git-clone-required": True,
246 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
248 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
249 ("chmod-R-a-w", "chmod -R a-w ."),
254 "git-clone-required": True,
256 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json" + samba_configure_params
),
258 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
259 ("chmod-R-a-w", "chmod -R a-w ."),
264 "git-clone-required": True,
266 ("configure", "./configure.developer --without-ad-dc --with-system-heimdalkrb5" + samba_configure_params
),
268 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
269 ("chmod-R-a-w", "chmod -R a-w ."),
273 "samba-no-opath-build": {
274 "git-clone-required": True,
276 ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1' ./configure.developer --without-ad-dc " + samba_configure_params
),
278 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
279 ("chmod-R-a-w", "chmod -R a-w ."),
283 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
286 ("random-sleep", random_sleep(300, 900)),
287 ("configure", "./configure.developer" + samba_configure_params
),
289 ("test", make_test(exclude_envs
=[
302 "ad_dc_default_smb1",
310 "ad_member_idmap_rid",
311 "admem_idmap_autorid",
312 "ad_member_idmap_ad",
320 "fileserver_smb1_done",
334 "ad_dc_default_smb1",
335 "ad_dc_default_smb1_done",
343 ("test-slow-none", make_test(cmd
='make test', TESTS
="--include=selftest/slow-none", include_envs
=["none"])),
345 ("install", "make install"),
346 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
347 ("clean", "make clean"),
351 # We have 'test' before 'install' because, 'test' should work without 'install (runs all the other envs)'
354 ("random-sleep", random_sleep(300, 900)),
355 ("configure", "./configure.developer --with-system-mitkrb5 --with-experimental-mit-ad-dc" + samba_configure_params
),
357 ("test", make_test(exclude_envs
=[
370 "ad_dc_default_smb1",
371 "ad_dc_default_smb1_done",
379 "ad_member_idmap_rid",
380 "admem_idmap_autorid",
381 "ad_member_idmap_ad",
389 "fileserver_smb1_done",
403 "ad_dc_default_smb1",
404 "ad_dc_default_smb1_done",
413 ("install", "make install"),
414 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
415 ("clean", "make clean"),
420 "dependency": "samba-nt4-build",
422 ("random-sleep", random_sleep(300, 900)),
423 ("test", make_test(include_envs
=[
432 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
436 "samba-fileserver": {
437 "dependency": "samba-h5l-build",
439 ("random-sleep", random_sleep(300, 900)),
440 ("test", make_test(include_envs
=[
443 "fileserver_smb1_done",
445 "ktest", # ktest is also tested in samba-ktest-mit samba
446 # and samba-mitkrb5 but is tested here against
450 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
454 # This is a full build without the AD DC so we test the build with
455 # MIT Kerberos from the current system. Runtime behaviour is
456 # confirmed via the ktest (static ccache and keytab) environment
460 ("random-sleep", random_sleep(300, 900)),
461 ("configure", "./configure.developer --without-ad-dc --with-system-mitkrb5 " + samba_configure_params
),
463 ("test", make_test(include_envs
=[
464 "ktest", # ktest is also tested in fileserver, samba and
465 # samba-mitkrb5 but is tested here against a
469 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
474 "dependency": "samba-def-build",
476 ("random-sleep", random_sleep(300, 900)),
477 ("test", make_test(include_envs
=[
479 "ad_member_idmap_rid",
480 "admem_idmap_autorid",
481 "ad_member_idmap_ad",
483 "ad_member_offlogon",
486 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
491 "dependency": "samba-no-opath-build",
493 ("random-sleep", random_sleep(300, 900)),
495 cmd
="make testonly DISABLE_OPATH=1",
505 ("check-clean-tree", "script/clean-source-tree.sh"),
510 "dependency": "samba-no-opath-build",
512 ("random-sleep", random_sleep(300, 900)),
514 cmd
="make testonly DISABLE_OPATH=1",
518 "fileserver_smb1_done",
521 ("check-clean-tree", "script/clean-source-tree.sh"),
526 "dependency": "samba-def-build",
528 ("random-sleep", random_sleep(1, 1)),
529 ("test", make_test(include_envs
=[
537 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
542 "dependency": "samba-def-build",
544 ("random-sleep", random_sleep(1, 1)),
545 ("test", make_test(include_envs
=[
551 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
556 "dependency": "samba-def-build",
558 ("random-sleep", random_sleep(1, 1)),
559 ("test", make_test(include_envs
=[
566 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
571 "dependency": "samba-def-build",
573 ("random-sleep", random_sleep(1, 1)),
574 ("test", make_test(include_envs
=[
580 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
584 "dependency": "samba-def-build",
586 ("random-sleep", random_sleep(1, 1)),
587 ("test", make_test(include_envs
=[
592 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
597 "dependency": "samba-def-build",
599 ("random-sleep", random_sleep(1, 1)),
600 ("test", make_test(include_envs
=[
601 "ad_dc_default", "ad_dc_default_smb1", "ad_dc_default_smb1_done"])),
603 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
608 "dependency": "samba-def-build",
610 ("random-sleep", random_sleep(1, 1)),
611 ("test", make_test(include_envs
=["ad_dc_slowtests", "ad_dc_backup"])),
613 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
617 "samba-schemaupgrade": {
618 "dependency": "samba-def-build",
620 ("random-sleep", random_sleep(1, 1)),
621 ("test", make_test(include_envs
=["schema_dc", "schema_pair_dc"])),
623 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
627 # We split out the ad_dc_ntvfs tests (which are long) so other test do not wait
628 # This is currently the longest task, so we don't randomly delay it.
629 "samba-ad-dc-ntvfs": {
630 "dependency": "samba-def-build",
632 ("random-sleep", random_sleep(1, 1)),
633 ("test", make_test(include_envs
=["ad_dc_ntvfs"])),
635 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
639 # Test fips compliance
641 "dependency": "samba-mit-build",
643 ("random-sleep", random_sleep(1, 1)),
644 ("test", make_test(include_envs
=["ad_dc_fips", "ad_member_fips"])),
645 # TODO: This seems to generate only an empty samba-fips.info ("lcov", LCOV_CMD),
646 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
650 # run the backup/restore testenvs separately as they're fairly standalone
651 # (and CI seems to max out at ~3 different DCs running at once)
653 "dependency": "samba-def-build",
655 ("random-sleep", random_sleep(300, 900)),
656 ("test", make_test(include_envs
=[
662 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
666 "dependency": "samba-def-build",
668 ("random-sleep", random_sleep(300, 900)),
669 ("test", make_test(include_envs
=[
675 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
680 "dependency": "samba-mit-build",
682 ("random-sleep", random_sleep(1, 1)),
683 ("test", make_test(include_envs
=[
685 "ad_member_idmap_rid",
686 "admem_idmap_autorid",
687 "ad_member_idmap_ad",
689 "ad_member_offlogon",
692 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
696 "samba-addc-mit-1": {
697 "dependency": "samba-mit-build",
699 ("random-sleep", random_sleep(1, 1)),
700 ("test", make_test(include_envs
=[
708 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
712 "samba-addc-mit-4a": {
713 "dependency": "samba-mit-build",
715 ("random-sleep", random_sleep(1, 1)),
716 ("test", make_test(include_envs
=[
722 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
725 "samba-addc-mit-4b": {
726 "dependency": "samba-mit-build",
728 ("random-sleep", random_sleep(1, 1)),
729 ("test", make_test(include_envs
=[
734 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
740 ("configure", "./configure.developer --abi-check-disable" + samba_configure_params
),
742 ("test", make_test(TESTS
="${TESTS}")),
747 # Test cross-compile infrastructure
750 ("random-sleep", random_sleep(900, 1500)),
751 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
),
752 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
753 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
),
754 ("verify-cross-execute-output", "grep '^Checking value of NSIG' ./bin-xe/cross-answers.txt"),
755 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
756 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
),
757 ("compare-results", "script/compare_cc_results.py "
758 "./bin/c4che/default{} "
759 "./bin-xe/c4che/default{} "
760 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX
]*3))),
761 ("modify-cross-answers", "sed -i.bak -e 's/^\\(Checking value of NSIG:\\) .*/\\1 \"1234\"/' ./bin-xe/cross-answers.txt"),
762 ("configure-cross-answers-modified", "./configure.developer --out ./bin-xa2 --cross-compile" \
763 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa2/ab" + samba_configure_params
),
764 ("verify-cross-answers", "test $(sed -n -e 's/VALUEOF_NSIG = \\(.*\\)/\\1/p' ./bin-xa2/c4che/default{})" \
765 " = \"'1234'\"".format(CACHE_SUFFIX
)),
766 ("invalidate-cross-answers", "sed -i.bak -e '/^Checking value of NSIG/d' ./bin-xe/cross-answers.txt"),
767 ("configure-cross-answers-fail", "./configure.developer --out ./bin-xa3 --cross-compile" \
768 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa3/ab" + samba_configure_params
+ \
773 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
776 ("random-sleep", random_sleep(300, 900)),
777 ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --abi-check-disable" + samba_configure_params
),
779 ("test", make_test(cmd
='make test', TESTS
="--exclude=selftest/slow-none", include_envs
=["none"])),
780 ("quicktest", make_test(cmd
='make quicktest', include_envs
=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
782 ("install", "make install"),
783 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
784 ("clean", "make clean"),
790 ("random-sleep", random_sleep(900, 1500)),
792 # make sure we have tdb around:
793 ("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}"),
794 ("tdb-make", "cd lib/tdb && make"),
795 ("tdb-install", "cd lib/tdb && make install"),
797 # build samba with cluster support (also building ctdb):
799 "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH "
800 "PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} "
801 "./configure.developer ${PREFIX} "
802 "--with-selftest-prefix=./bin/ab "
803 "--with-cluster-support "
805 "--bundled-libraries=!tdb"),
806 ("samba-make", "make"),
807 ("samba-check", "./bin/smbd --configfile=/dev/null -b | grep CLUSTER_SUPPORT"),
808 ("samba-install", "make install"),
809 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd"),
813 INJECT_SELFTEST_PREFIX
=0,
814 include_envs
=["clusteredmember"])
818 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
819 ("clean", "make clean"),
820 ("ctdb-clean", "cd ./ctdb && make clean"),
826 ("random-sleep", random_sleep(300, 900)),
827 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
),
828 ("talloc-make", "cd lib/talloc && make"),
829 ("talloc-install", "cd lib/talloc && make install"),
831 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
),
832 ("tdb-make", "cd lib/tdb && make"),
833 ("tdb-install", "cd lib/tdb && make install"),
835 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
),
836 ("tevent-make", "cd lib/tevent && make"),
837 ("tevent-install", "cd lib/tevent && make install"),
839 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
),
840 ("ldb-make", "cd lib/ldb && make"),
841 ("ldb-install", "cd lib/ldb && make install"),
843 ("nondevel-configure", samba_libs_envvars
+ " ./configure ${PREFIX}"),
844 ("nondevel-make", "make -j"),
845 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"),
846 ("nondevel-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
847 ("nondevel-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
848 ("nondevel-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
849 ("nondevel-no-libldb", "find ./bin | grep -v 'module' | grep -v 'libldbsamba' | grep 'libldb' && exit 1; exit 0"),
850 ("nondevel-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
851 ("nondevel-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
852 ("nondevel-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
853 ("nondevel-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
854 ("nondevel-no-public-nss_winbind",
855 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
856 ("nondevel-no-public-nss_wins",
857 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
858 ("nondevel-no-public-libwbclient",
859 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
860 ("nondevel-no-public-pam_winbind",
861 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
862 ("nondevel-no-public-winbind_krb5_locator",
863 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
864 ("nondevel-no-public-async_dns_krb5_locator",
865 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
866 ("nondevel-install", "make -j install"),
867 ("nondevel-dist", "make dist"),
869 ("prefix-no-private-libtalloc", "find ${PREFIX_DIR} | grep -v 'libtalloc-report' | grep 'private.*libtalloc' && exit 1; exit 0"),
870 ("prefix-no-private-libtdb", "find ${PREFIX_DIR} | grep -v 'libtdb-wrap' | grep 'private.*libtdb' && exit 1; exit 0"),
871 ("prefix-no-private-libtevent", "find ${PREFIX_DIR} | grep -v 'libtevent-util' | grep 'private.*libtevent' && exit 1; exit 0"),
872 ("prefix-no-private-libldb", "find ${PREFIX_DIR} | grep -v 'module' | grep -v 'libldbsamba' | grep 'private.*libldb' && exit 1; exit 0"),
873 ("prefix-no-samba-nss_winbind", "ldd ${PREFIX_DIR}/lib/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
874 ("prefix-no-samba-nss_wins", "ldd ${PREFIX_DIR}/lib/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
875 ("prefix-no-samba-libwbclient", "ldd ${PREFIX_DIR}/lib/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
876 ("prefix-no-samba-pam_winbind", "ldd ${PREFIX_DIR}/lib/security/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
877 ("prefix-no-public-nss_winbind",
878 check_symbols("${PREFIX_DIR}/lib/libnss_winbind.so.2", "_nss_winbind_")),
879 ("prefix-no-public-nss_wins",
880 check_symbols("${PREFIX_DIR}/lib/libnss_wins.so.2", "_nss_wins_")),
881 ("prefix-no-public-libwbclient",
882 check_symbols("${PREFIX_DIR}/lib/libwbclient.so.0", "wbc")),
883 ("prefix-no-public-pam_winbind",
884 check_symbols("${PREFIX_DIR}/lib/security/pam_winbind.so", "pam_sm_")),
885 ("prefix-no-public-winbind_krb5_locator",
886 check_symbols("${PREFIX_DIR}/lib/krb5/winbind_krb5_locator.so",
888 ("prefix-no-public-async_dns_krb5_locator",
889 check_symbols("${PREFIX_DIR}/lib/krb5/async_dns_krb5_locator.so",
892 # retry with all modules shared
893 ("allshared-distclean", "make distclean"),
894 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL"),
895 ("allshared-make", "make -j"),
896 ("allshared-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"),
897 ("allshared-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"),
898 ("allshared-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"),
899 ("allshared-no-libldb", "find ./bin | grep -v 'module' | grep -v 'libldbsamba' | grep 'libldb' && exit 1; exit 0"),
900 ("allshared-no-samba-nss_winbind", "ldd ./bin/plugins/libnss_winbind.so.2 | grep 'samba' && exit 1; exit 0"),
901 ("allshared-no-samba-nss_wins", "ldd ./bin/plugins/libnss_wins.so.2 | grep 'samba' && exit 1; exit 0"),
902 ("allshared-no-samba-libwbclient", "ldd ./bin/shared/libwbclient.so.0 | grep 'samba' && exit 1; exit 0"),
903 ("allshared-no-samba-pam_winbind", "ldd ./bin/plugins/pam_winbind.so | grep -v 'libtalloc.so.2' | grep 'samba' && exit 1; exit 0"),
904 ("allshared-no-public-nss_winbind",
905 check_symbols("./bin/plugins/libnss_winbind.so.2", "_nss_winbind_")),
906 ("allshared-no-public-nss_wins",
907 check_symbols("./bin/plugins/libnss_wins.so.2", "_nss_wins_")),
908 ("allshared-no-public-libwbclient",
909 check_symbols("./bin/shared/libwbclient.so.0", "wbc")),
910 ("allshared-no-public-pam_winbind",
911 check_symbols("./bin/plugins/pam_winbind.so", "pam_sm_")),
912 ("allshared-no-public-winbind_krb5_locator",
913 check_symbols("./bin/plugins/winbind_krb5_locator.so", "service_locator")),
914 ("allshared-no-public-async_dns_krb5_locator",
915 check_symbols("./bin/plugins/async_dns_krb5_locator.so", "service_locator")),
921 # build the fuzzers (static) via the oss-fuzz script
922 ("fuzzers-mkdir-prefix", "mkdir -p ${PREFIX_DIR}"),
923 ("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"),
927 # * Test smbd and smbtorture can build semi-static
929 # * Test Samba without python still builds.
931 # When this test fails due to more use of Python, the expectations
932 # is that the newly failing part of the code should be disabled
933 # when --disable-python is set (rather than major work being done
934 # to support this environment).
936 # The target here is for vendors shipping a minimal smbd.
937 "samba-minimal-smbd": {
939 ("random-sleep", random_sleep(300, 900)),
941 # build with all modules static
942 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL"),
943 ("allstatic-make", "make -j"),
944 ("allstatic-test", make_test(TESTS
="samba3.smb2.create.*nt4_dc")),
945 ("allstatic-lcov", LCOV_CMD
),
947 # retry with nonshared smbd and smbtorture
948 ("nonshared-distclean", "make distclean"),
949 ("nonshared-configure", "./configure.developer " + samba_configure_params
+ " --bundled-libraries=ALL --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd"),
950 ("nonshared-make", "make -j"),
951 # TODO ("nonshared-test", make_test(TESTS="samba3.smb2.create.*nt4_dc")),
952 # TODO ("nonshared-lcov", LCOV_CMD),
954 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
955 ("clean", "make clean"),
961 ("random-sleep", random_sleep(300, 900)),
963 ("configure", "./configure.developer ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --disable-python --without-ad-dc"),
965 ("find-python", "script/find_python.sh ${PREFIX}"),
966 ("test", "make test-nopython"),
968 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
969 ("clean", "make clean"),
971 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
972 ("talloc-make", "cd lib/talloc && make"),
973 ("talloc-install", "cd lib/talloc && make install"),
975 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
976 ("tdb-make", "cd lib/tdb && make"),
977 ("tdb-install", "cd lib/tdb && make install"),
979 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
980 ("tevent-make", "cd lib/tevent && make"),
981 ("tevent-install", "cd lib/tevent && make install"),
983 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python"),
984 ("ldb-make", "cd lib/ldb && make"),
985 ("ldb-install", "cd lib/ldb && make install"),
987 # retry against installed library packages, but no required modules
988 ("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"),
989 ("libs-make", "make -j"),
990 ("libs-install", "make install"),
991 ("libs-check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
992 ("libs-clean", "make clean"),
999 ("random-sleep", random_sleep(60, 600)),
1000 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1002 ("install", "make install"),
1003 ("test", "make test"),
1005 ("clean", "make clean"),
1006 ("configure-no-lmdb", "./configure ${ENABLE_COVERAGE} --enable-developer --without-ldb-lmdb -C ${PREFIX}"),
1007 ("make-no-lmdb", "make"),
1008 ("test-no-lmdb", "make test"),
1009 ("lcov-no-lmdb", LCOV_CMD
),
1010 ("install-no-lmdb", "make install"),
1011 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1012 ("distcheck", "make distcheck"),
1013 ("clean", "make clean"),
1019 ("random-sleep", random_sleep(60, 600)),
1020 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1022 ("install", "make install"),
1023 ("test", "make test"),
1025 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1026 ("distcheck", "make distcheck"),
1027 ("clean", "make clean"),
1033 ("random-sleep", random_sleep(60, 600)),
1034 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1036 ("install", "make install"),
1037 ("test", "make test"),
1039 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1040 ("distcheck", "make distcheck"),
1041 ("clean", "make clean"),
1047 ("random-sleep", random_sleep(60, 600)),
1048 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1050 ("install", "make install"),
1051 ("test", "make test"),
1053 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1054 ("distcheck", "make distcheck"),
1055 ("clean", "make clean"),
1061 ("random-sleep", random_sleep(60, 600)),
1062 ("configure", "./configure ${ENABLE_COVERAGE} --enable-developer -C ${PREFIX}"),
1064 ("install", "make install"),
1065 ("test", "make test"),
1067 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1068 ("distcheck", "make distcheck"),
1069 ("clean", "make clean"),
1074 "git-clone-required": True,
1076 ("random-sleep", random_sleep(60, 600)),
1077 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}"),
1078 ("touch", "touch *.yp"),
1080 ("test", "make test"),
1081 ("install", "make install"),
1082 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm"),
1083 ("check-clean-tree", CLEAN_SOURCE_TREE_CMD
),
1084 ("clean", "make clean"),
1088 # these are useful for debugging autobuild
1091 ("pass", 'echo passing && /bin/true'),
1096 ("fail", 'echo failing && /bin/false'),
1101 defaulttasks
= list(tasks
.keys())
1103 defaulttasks
.remove("pass")
1104 defaulttasks
.remove("fail")
1106 # The build tasks will be brought in by the test tasks as needed
1107 defaulttasks
.remove("samba-def-build")
1108 defaulttasks
.remove("samba-nt4-build")
1109 defaulttasks
.remove("samba-mit-build")
1110 defaulttasks
.remove("samba-h5l-build")
1111 defaulttasks
.remove("samba-no-opath-build")
1113 # This is not a normal test, but a task to support manually running
1114 # one test under autobuild
1115 defaulttasks
.remove("samba-test-only")
1117 # Only built on GitLab CI and not in the default autobuild because it
1118 # uses too much space (4GB of semi-static binaries)
1119 defaulttasks
.remove("samba-fuzz")
1121 # The FIPS build runs only in GitLab CI on a current Fedora Docker
1122 # container where a simulated FIPS mode is possible.
1123 defaulttasks
.remove("samba-fips")
1125 # The MIT build runs on a current Fedora where an up to date MIT KDC
1126 # is already packaged. This avoids needing to backport a current MIT
1127 # to the default Ubuntu 18.04, particularly during development, and
1128 # the need to install on the shared sn-devel-184.
1130 defaulttasks
.remove("samba-mitkrb5")
1131 defaulttasks
.remove("samba-admem-mit")
1132 defaulttasks
.remove("samba-addc-mit-1")
1133 defaulttasks
.remove("samba-addc-mit-4a")
1134 defaulttasks
.remove("samba-addc-mit-4b")
1136 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
1137 defaulttasks
.remove("samba-o3")
1146 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
1148 show
= options
.verbose
1150 do_print("Running: '%s' in '%s'" % (cmd
, dir))
1152 out
= check_output([cmd
], shell
=True, cwd
=dir)
1153 return out
.decode(encoding
='utf-8', errors
='backslashreplace')
1155 return check_call(cmd
, shell
=True, cwd
=dir)
1157 return call(cmd
, shell
=True, cwd
=dir)
1159 def rmdir_force(dirname
, re_raise
=True):
1161 run_cmd("test -d %s && chmod -R +w %s; rm -rf %s" % (
1162 dirname
, dirname
, dirname
), output
=True, show
=True)
1163 except CalledProcessError
as e
:
1164 do_print("Failed: '%s'" % (str(e
)))
1165 run_cmd("tree %s" % dirname
, output
=True, show
=True)
1171 class builder(object):
1172 '''handle build of one directory'''
1174 def __init__(self
, name
, definition
):
1176 self
.dir = builddirs
.get(name
, '.')
1177 self
.tag
= self
.name
.replace('/', '_')
1178 self
.definition
= definition
1179 self
.sequence
= definition
["sequence"]
1180 self
.git_clone_required
= False
1181 if "git-clone-required" in definition
:
1182 self
.git_clone_required
= bool(definition
["git-clone-required"])
1186 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
1187 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
1189 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
1190 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
1191 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
1192 self
.stdout
= open(self
.stdout_path
, 'w')
1193 self
.stderr
= open(self
.stderr_path
, 'w')
1194 self
.stdin
= open("/dev/null", 'r')
1195 self
.builder_dir
= "%s/%s" % (testbase
, self
.tag
)
1196 self
.test_source_dir
= self
.builder_dir
1197 self
.cwd
= "%s/%s" % (self
.builder_dir
, self
.dir)
1198 self
.selftest_prefix
= "%s/bin/ab" % (self
.cwd
)
1199 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
1201 self
.producer
= None
1203 if self
.git_clone_required
:
1204 assert "dependency" not in definition
1206 def mark_existing(self
):
1207 do_print('%s: Mark as existing dependency' % self
.name
)
1208 self
.next
= len(self
.sequence
)
1211 def add_consumer(self
, consumer
):
1212 do_print("%s: add consumer: %s" % (self
.name
, consumer
.name
))
1213 consumer
.producer
= self
1214 consumer
.test_source_dir
= self
.test_source_dir
1215 self
.consumers
.append(consumer
)
1217 def start_next(self
):
1218 if self
.producer
is not None:
1219 if not self
.producer
.done
:
1220 do_print("%s: Waiting for producer: %s" % (self
.name
, self
.producer
.name
))
1224 rmdir_force(self
.builder_dir
)
1225 rmdir_force(self
.prefix
)
1226 if self
.producer
is not None:
1227 run_cmd("mkdir %s" % (self
.builder_dir
), dir=test_master
, show
=True)
1228 elif not self
.git_clone_required
:
1229 run_cmd("cp -R -a -l %s %s" % (test_master
, self
.builder_dir
), dir=test_master
, show
=True)
1231 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.builder_dir
), dir=test_master
, show
=True)
1233 if self
.next
== len(self
.sequence
):
1235 do_print('%s: Completed OK' % self
.name
)
1237 if not options
.nocleanup
and len(self
.consumers
) == 0:
1238 do_print('%s: Cleaning up' % self
.name
)
1239 rmdir_force(self
.builder_dir
)
1240 rmdir_force(self
.prefix
)
1241 for consumer
in self
.consumers
:
1242 if consumer
.next
!= 0:
1244 do_print('%s: Starting consumer %s' % (self
.name
, consumer
.name
))
1245 consumer
.start_next()
1246 if self
.producer
is not None:
1247 self
.producer
.consumers
.remove(self
)
1248 assert self
.producer
.done
1249 self
.producer
.start_next()
1250 do_print('%s: Remaining consumers %u' % (self
.name
, len(self
.consumers
)))
1252 (self
.stage
, self
.cmd
) = self
.sequence
[self
.next
]
1253 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific
=1, standard_lib
=0, prefix
=self
.prefix
))
1254 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
1255 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
1256 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
1257 self
.cmd
= self
.cmd
.replace("${TEST_SOURCE_DIR}", self
.test_source_dir
)
1258 self
.cmd
= self
.cmd
.replace("${SELFTEST_PREFIX}", self
.selftest_prefix
)
1259 self
.cmd
= self
.cmd
.replace("${LOG_BASE}", options
.log_base
)
1260 self
.cmd
= self
.cmd
.replace("${NAME}", self
.name
)
1261 self
.cmd
= self
.cmd
.replace("${ENABLE_COVERAGE}", options
.enable_coverage
)
1262 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, self
.cwd
))
1263 self
.proc
= Popen(self
.cmd
, shell
=True,
1264 close_fds
=True, cwd
=self
.cwd
,
1265 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
1268 def expand_dependencies(n
):
1270 if "dependency" in tasks
[n
]:
1271 depname
= tasks
[n
]["dependency"]
1272 assert depname
in tasks
1273 sdeps
= expand_dependencies(depname
)
1274 assert n
not in sdeps
1277 deps
.append(depname
)
1281 class buildlist(object):
1282 '''handle build of multiple directories'''
1284 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
1285 self
.tail_proc
= None
1288 if options
.restrict_tests
:
1289 tasknames
= ["samba-test-only"]
1291 tasknames
= defaulttasks
1293 given_tasknames
= tasknames
.copy()
1294 implicit_tasknames
= []
1295 for n
in given_tasknames
:
1296 deps
= expand_dependencies(n
)
1298 if dep
in given_tasknames
:
1300 if dep
in implicit_tasknames
:
1302 implicit_tasknames
.append(dep
)
1304 tasknames
= implicit_tasknames
.copy()
1305 tasknames
.extend(given_tasknames
)
1306 do_print("given_tasknames: %s" % given_tasknames
)
1307 do_print("implicit_tasknames: %s" % implicit_tasknames
)
1308 do_print("tasknames: %s" % tasknames
)
1309 self
.tlist
= [builder(n
, tasks
[n
]) for n
in tasknames
]
1312 rebase_remote
= "rebaseon"
1314 "git-clone-required": True,
1318 git remote add -t %s %s %s
1322 git describe %s/%s > old_remote_branch.desc
1324 git describe %s/%s > remote_branch.desc
1325 diff old_remote_branch.desc remote_branch.desc
1328 rebase_branch
, rebase_remote
, rebase_url
,
1330 rebase_remote
, rebase_branch
,
1332 rebase_remote
, rebase_branch
1335 self
.retry
= builder('retry', retry_task
)
1336 self
.need_retry
= False
1338 if options
.skip_dependencies
:
1339 for b
in self
.tlist
:
1340 if b
.name
in implicit_tasknames
:
1343 for b
in self
.tlist
:
1344 do_print("b.name=%s" % b
.name
)
1345 if "dependency" not in b
.definition
:
1347 depname
= b
.definition
["dependency"]
1348 do_print("b.name=%s: dependency:%s" % (b
.name
, depname
))
1349 for p
in self
.tlist
:
1350 if p
.name
== depname
:
1353 def kill_kids(self
):
1354 if self
.tail_proc
is not None:
1355 self
.tail_proc
.terminate()
1356 self
.tail_proc
.wait()
1357 self
.tail_proc
= None
1358 if self
.retry
is not None:
1359 self
.retry
.proc
.terminate()
1360 self
.retry
.proc
.wait()
1362 for b
in self
.tlist
:
1363 if b
.proc
is not None:
1364 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.test_source_dir
, checkfail
=False)
1372 for b
in self
.tlist
:
1375 none_running
= False
1376 b
.status
= b
.proc
.poll()
1377 if b
.status
is None:
1382 ret
= self
.retry
.proc
.poll()
1384 self
.need_retry
= True
1392 for b
in self
.tlist
:
1395 self
.retry
.start_next()
1398 if options
.retry
and self
.need_retry
:
1400 do_print("retry needed")
1401 return (0, None, None, None, "retry")
1404 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
1406 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
1409 return (0, None, None, None, "All OK")
1411 def write_system_info(self
, filename
):
1412 with
open(filename
, 'w') as f
:
1413 for cmd
in ['uname -a',
1417 'cat /proc/cpuinfo',
1420 'df -m %s' % testbase
]:
1422 out
= run_cmd(cmd
, output
=True, checkfail
=False)
1423 except CalledProcessError
as e
:
1424 out
= "<failed: %s>" % str(e
)
1425 print('### %s' % cmd
, file=f
)
1429 def tarlogs(self
, fname
):
1430 with tarfile
.open(fname
, "w:gz") as tar
:
1431 for b
in self
.tlist
:
1432 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
1433 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
1434 if os
.path
.exists("autobuild.log"):
1435 tar
.add("autobuild.log")
1436 filename
= 'system-info.txt'
1437 self
.write_system_info(filename
)
1440 def remove_logs(self
):
1441 for b
in self
.tlist
:
1442 os
.unlink(b
.stdout_path
)
1443 os
.unlink(b
.stderr_path
)
1445 def start_tail(self
):
1446 cmd
= ["tail", "-f"]
1447 for b
in self
.tlist
:
1448 cmd
.append(b
.stdout_path
)
1449 cmd
.append(b
.stderr_path
)
1450 self
.tail_proc
= Popen(cmd
, close_fds
=True)
1453 def cleanup(do_raise
=False):
1454 if options
.nocleanup
:
1456 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
1457 run_cmd("stat %s" % testbase
, show
=True)
1458 do_print("Cleaning up %r" % cleanup_list
)
1459 for d
in cleanup_list
:
1460 ok
= rmdir_force(d
, re_raise
=False)
1463 if os
.path
.isdir(d
):
1464 do_print("Killing, waiting and retry")
1465 run_cmd("killbysubdir %s > /dev/null 2>&1" % d
, checkfail
=False)
1467 do_print("Waiting and retry")
1469 rmdir_force(d
, re_raise
=do_raise
)
1472 def daemonize(logfile
):
1474 if pid
== 0: # Parent
1477 if pid
!= 0: # Actual daemon
1482 import resource
# Resource usage information.
1483 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
1484 if maxfd
== resource
.RLIM_INFINITY
:
1485 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
1486 for fd
in range(0, maxfd
):
1491 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
1496 def write_pidfile(fname
):
1497 '''write a pid file, cleanup on exit'''
1498 with
open(fname
, mode
='w') as f
:
1499 f
.write("%u\n" % os
.getpid())
1502 def rebase_tree(rebase_url
, rebase_branch
="master"):
1503 rebase_remote
= "rebaseon"
1504 do_print("Rebasing on %s" % rebase_url
)
1505 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1506 run_cmd("git remote add -t %s %s %s" %
1507 (rebase_branch
, rebase_remote
, rebase_url
),
1508 show
=True, dir=test_master
)
1509 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
1510 if options
.fix_whitespace
:
1511 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
1512 (rebase_remote
, rebase_branch
),
1513 show
=True, dir=test_master
)
1515 run_cmd("git rebase --force-rebase %s/%s" %
1516 (rebase_remote
, rebase_branch
),
1517 show
=True, dir=test_master
)
1518 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
1519 (rebase_remote
, rebase_branch
),
1520 dir=test_master
, output
=True)
1522 do_print("No differences between HEAD and %s/%s - exiting" %
1523 (rebase_remote
, rebase_branch
))
1525 run_cmd("git describe %s/%s" %
1526 (rebase_remote
, rebase_branch
),
1527 show
=True, dir=test_master
)
1528 run_cmd("git describe HEAD", show
=True, dir=test_master
)
1529 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
1530 (rebase_remote
, rebase_branch
),
1531 show
=True, dir=test_master
)
1534 def push_to(push_url
, push_branch
="master"):
1535 push_remote
= "pushto"
1536 do_print("Pushing to %s" % push_url
)
1538 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
1539 run_cmd("git commit --amend -c HEAD", dir=test_master
)
1540 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
1541 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
1542 run_cmd("git remote add -t %s %s %s" %
1543 (push_branch
, push_remote
, push_url
),
1544 show
=True, dir=test_master
)
1545 run_cmd("git push %s +HEAD:%s" %
1546 (push_remote
, push_branch
),
1547 show
=True, dir=test_master
)
1550 def send_email(subject
, text
, log_tar
):
1551 if options
.email
is None:
1552 do_print("not sending email because the recipient is not set")
1553 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
1556 outer
= MIMEMultipart()
1557 outer
['Subject'] = subject
1558 outer
['To'] = options
.email
1559 outer
['From'] = options
.email_from
1560 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
1561 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
1562 outer
.attach(MIMEText(text
, 'plain', 'utf-8'))
1563 if options
.attach_logs
:
1564 with
open(log_tar
, 'rb') as fp
:
1565 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
1566 # Set the filename parameter
1567 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
1569 content
= outer
.as_string()
1570 s
= smtplib
.SMTP(options
.email_server
)
1571 email_user
= os
.getenv('SMTP_USERNAME')
1572 email_password
= os
.getenv('SMTP_PASSWORD')
1573 if email_user
is not None:
1575 s
.login(email_user
, email_password
)
1577 s
.sendmail(options
.email_from
, [options
.email
], content
)
1582 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1583 elapsed_time
, log_base
=None, add_log_tail
=True):
1584 '''send an email to options.email about the failure'''
1585 elapsed_minutes
= elapsed_time
/ 60.0
1586 if log_base
is None:
1591 Your autobuild on %s failed after %.1f minutes
1592 when trying to test %s with the following error:
1596 the autobuild has been abandoned. Please fix the error and resubmit.
1598 A summary of the autobuild process is here:
1601 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
1603 if options
.restrict_tests
:
1605 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1607 if failed_task
!= 'rebase':
1609 You can see logs of the failed task here:
1614 or you can get full logs of all tasks in this job here:
1618 The top commit for the tree that was built was:
1622 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
1625 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
1626 lines
= f
.readlines()
1627 log_tail
= "".join(lines
[-50:])
1628 num_lines
= len(lines
)
1630 # Also include stderr (compile failures) if < 50 lines of stdout
1631 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
1632 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
1635 The last 50 lines of log messages:
1641 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1642 send_email('autobuild[%s] failure on %s for task %s during %s'
1643 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
1647 def email_success(elapsed_time
, log_base
=None):
1648 '''send an email to options.email about a successful build'''
1649 if log_base
is None:
1654 Your autobuild on %s has succeeded after %.1f minutes.
1656 ''' % (platform
.node(), elapsed_time
/ 60.)
1658 if options
.restrict_tests
:
1660 The build was restricted to tests matching %s\n""" % options
.restrict_tests
1662 if options
.keeplogs
:
1665 you can get full logs of all tasks in this job here:
1672 The top commit for the tree that was built was:
1675 ''' % top_commit_msg
1677 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
1678 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
1682 # get the top commit message, for emails
1683 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
1686 if options
.skip_dependencies
:
1687 run_cmd("stat %s" % testbase
, dir=testbase
, output
=True)
1689 os
.makedirs(testbase
)
1690 except Exception as reason
:
1691 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
1692 cleanup_list
.append(testbase
)
1695 logfile
= os
.path
.join(testbase
, "log")
1696 do_print("Forking into the background, writing progress to %s" % logfile
)
1699 write_pidfile(gitroot
+ "/autobuild.pid")
1701 start_time
= time
.time()
1705 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
1706 os
.makedirs(test_tmpdir
)
1707 # The waf uninstall code removes empty directories all the way
1708 # up the tree. Creating a file in test_tmpdir stops it from
1710 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
1711 ".directory-is-not-empty"), show
=True)
1712 run_cmd("stat %s" % test_tmpdir
, show
=True)
1713 run_cmd("stat %s" % testbase
, show
=True)
1714 if options
.skip_dependencies
:
1715 run_cmd("stat %s" % test_master
, dir=testbase
, output
=True)
1717 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
1723 if options
.rebase
is not None:
1724 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
1726 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1728 elapsed_time
= time
.time() - start_time
1729 email_failure(-1, 'rebase', 'rebase', 'rebase',
1730 'rebase on %s failed' % options
.branch
,
1731 elapsed_time
, log_base
=options
.log_base
)
1735 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
1738 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
1739 if status
!= 0 or errstr
!= "retry":
1741 cleanup(do_raise
=True)
1746 cleanup_list
.append(gitroot
+ "/autobuild.pid")
1752 do_print("waiting for tail to flush")
1755 elapsed_time
= time
.time() - start_time
1757 if options
.passcmd
is not None:
1758 do_print("Running passcmd: %s" % options
.passcmd
)
1759 run_cmd(options
.passcmd
, dir=test_master
)
1760 if options
.pushto
is not None:
1761 push_to(options
.pushto
, push_branch
=options
.branch
)
1762 if options
.keeplogs
or options
.attach_logs
:
1763 blist
.tarlogs("logs.tar.gz")
1764 do_print("Logs in logs.tar.gz")
1765 if options
.always_email
:
1766 email_success(elapsed_time
, log_base
=options
.log_base
)
1772 # something failed, gather a tar of the logs
1773 blist
.tarlogs("logs.tar.gz")
1775 if options
.email
is not None:
1776 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1777 elapsed_time
, log_base
=options
.log_base
)
1779 elapsed_minutes
= elapsed_time
/ 60.0
1782 ####################################################################
1786 Your autobuild[%s] on %s failed after %.1f minutes
1787 when trying to test %s with the following error:
1791 the autobuild has been abandoned. Please fix the error and resubmit.
1793 ####################################################################
1795 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1799 do_print("Logs in logs.tar.gz")