2 # run tests on all Samba subprojects and push to a git tree on success
3 # Copyright Andrew Tridgell 2010
4 # released under GNU GPL v3 or later
6 from __future__
import print_function
7 from subprocess
import call
, check_call
, Popen
, PIPE
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'
42 "samba-fileserver": ".",
47 "samba-libs-py3": ".",
49 "samba-none-env": ".",
50 "samba-none-env-py3": ".",
52 "samba-ad-dc-py3": ".",
54 "samba-ad-dc-2-py3": ".",
55 "samba-systemkrb5": ".",
56 "samba-nopython": ".",
59 "talloc": "lib/talloc",
60 "replace": "lib/replace",
61 "tevent": "lib/tevent",
65 defaulttasks
= builddirs
.keys()
67 if os
.environ
.get("AUTOBUILD_SKIP_SAMBA_O3", "0") == "1":
68 defaulttasks
.remove("samba-o3")
70 ctdb_configure_params
= " --enable-developer --picky-developer ${PREFIX}"
71 samba_configure_params
= " --picky-developer ${PREFIX} ${EXTRA_PYTHON} --with-profiling-data"
73 samba_libs_envvars
= "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH"
74 samba_libs_envvars
+= " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
75 samba_libs_envvars
+= " ADDITIONAL_CFLAGS='-Wmissing-prototypes'"
76 samba_libs_configure_base
= samba_libs_envvars
+ " ./configure --abi-check --enable-debug --picky-developer -C ${PREFIX}"
77 samba_libs_configure_libs
= samba_libs_configure_base
+ " --bundled-libraries=cmocka,popt,NONE ${EXTRA_PYTHON}"
78 samba_libs_configure_bundled_libs
= " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
79 samba_libs_configure_samba
= samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " ${EXTRA_PYTHON}"
81 if os
.environ
.get("AUTOBUILD_NO_EXTRA_PYTHON", "0") == "1":
84 extra_python
= "--extra-python=/usr/bin/python3"
87 "ctdb": [("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
88 ("configure", "./configure " + ctdb_configure_params
, "text/plain"),
89 ("make", "make all", "text/plain"),
90 ("install", "make install", "text/plain"),
91 ("test", "make autotest", "text/plain"),
92 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
93 ("clean", "make clean", "text/plain")],
95 # We have 'test' before 'install' because, 'test' should work without 'install (runs ad_dc_ntvfs and all the other envs)'
96 "samba": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
97 ("make", "make -j", "text/plain"),
98 ("test", "make test FAIL_IMMEDIATELY=1 "
100 "--exclude-env=none "
101 "--exclude-env=nt4_dc "
102 "--exclude-env=nt4_member "
103 "--exclude-env=ad_dc "
104 "--exclude-env=ad_dc_no_nss "
105 "--exclude-env=fl2003dc "
106 "--exclude-env=fl2008r2dc "
107 "--exclude-env=ad_member "
108 "--exclude-env=ad_member_idmap_rid "
109 "--exclude-env=ad_member_idmap_ad "
110 "--exclude-env=chgdcpass "
111 "--exclude-env=vampire_2000_dc "
112 "--exclude-env=fl2000dc "
113 "--exclude-env=fileserver "
114 "--exclude-env=backupfromdc "
115 "--exclude-env=restoredc "
116 "--exclude-env=renamedc "
117 "--exclude-env=offlinebackupdc "
118 "--exclude-env=labdc "
121 ("install", "make install", "text/plain"),
122 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
123 ("clean", "make clean", "text/plain")],
125 # We split out this so the isolated nt4_dc tests do not wait for ad_dc or ad_dc_ntvfs tests (which are long)
126 "samba-nt4": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
127 ("configure", "./configure.developer --without-ads --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
128 ("make", "make -j", "text/plain"),
129 ("test", "make test FAIL_IMMEDIATELY=1 "
131 "--include-env=nt4_dc --include-env=nt4_member'", "text/plain"),
132 ("install", "make install", "text/plain"),
133 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
134 ("clean", "make clean", "text/plain")],
136 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
137 "samba-fileserver": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
138 ("configure", "./configure.developer --without-ad-dc --without-ldap --without-ads --without-json-audit --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
139 ("make", "make -j", "text/plain"),
140 ("test", "make test FAIL_IMMEDIATELY=1 "
142 "--include-env=fileserver'", "text/plain"),
143 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
145 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
146 "samba-ad-dc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
147 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
148 ("make", "make -j", "text/plain"),
149 ("test", "make test FAIL_IMMEDIATELY=1 "
151 "--include-env=ad_dc "
152 "--include-env=fl2003dc "
153 "--include-env=fl2008r2dc "
154 "--include-env=ad_member "
155 "--include-env=ad_member_idmap_rid "
156 "--include-env=ad_member_idmap_ad'", "text/plain"),
157 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
159 # We split out this so the isolated ad_dc tests do not wait for ad_dc_ntvfs tests (which are long)
160 "samba-ad-dc-2": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
161 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
162 ("make", "make -j", "text/plain"),
163 ("test", "make test FAIL_IMMEDIATELY=1 "
165 "--include-env=chgdcpass "
166 "--include-env=vampire_2000_dc "
167 "--include-env=fl2000dc "
168 "--include-env=ad_dc_no_nss "
169 "--include-env=backupfromdc "
170 "--include-env=restoredc "
171 "--include-env=renamedc "
172 "--include-env=offlinebackupdc "
173 "--include-env=labdc "
176 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain")],
178 "samba-test-only": [("configure", "./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
179 ("make", "make -j", "text/plain"),
180 ("test", 'make test FAIL_IMMEDIATELY=1 TESTS="${TESTS}"', "text/plain")],
182 # Test cross-compile infrastructure
183 "samba-xc": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
184 ("configure-native", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
185 ("configure-cross-execute", "./configure.developer --out ./bin-xe --cross-compile --cross-execute=script/identity_cc.sh" \
186 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xe/ab" + samba_configure_params
, "text/plain"),
187 ("configure-cross-answers", "./configure.developer --out ./bin-xa --cross-compile" \
188 " --cross-answers=./bin-xe/cross-answers.txt --with-selftest-prefix=./bin-xa/ab" + samba_configure_params
, "text/plain"),
189 ("compare-results", "script/compare_cc_results.py "
190 "./bin/c4che/default{} "
191 "./bin-xe/c4che/default{} "
192 "./bin-xa/c4che/default{}".format(*([CACHE_SUFFIX
]*3)), "text/plain")],
194 # test build with -O3 -- catches extra warnings and bugs, tests the ad_dc environments
195 "samba-o3": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
196 ("configure", "ADDITIONAL_CFLAGS='-O3' ./configure.developer --with-selftest-prefix=./bin/ab --abi-check-disable" + samba_configure_params
, "text/plain"),
197 ("make", "make -j", "text/plain"),
198 ("test", "make quicktest FAIL_IMMEDIATELY=1 "
200 "--include-env=ad_dc'", "text/plain"),
201 ("install", "make install", "text/plain"),
202 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
203 ("clean", "make clean", "text/plain")],
205 "samba-ctdb": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
207 # make sure we have tdb around:
208 ("tdb-configure", "cd lib/tdb && PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig ./configure --bundled-libraries=NONE --abi-check --enable-debug -C ${PREFIX}", "text/plain"),
209 ("tdb-make", "cd lib/tdb && make", "text/plain"),
210 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
213 # build samba with cluster support (also building ctdb):
214 ("samba-configure", "PYTHONPATH=${PYTHON_PREFIX}/site-packages:$PYTHONPATH PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH} ./configure.developer --picky-developer ${PREFIX} --with-selftest-prefix=./bin/ab --with-cluster-support --bundled-libraries=!tdb", "text/plain"),
215 ("samba-make", "make", "text/plain"),
216 ("samba-check", "./bin/smbd -b | grep CLUSTER_SUPPORT", "text/plain"),
217 ("samba-install", "make install", "text/plain"),
218 ("ctdb-check", "test -e ${PREFIX_DIR}/sbin/ctdbd", "text/plain"),
221 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
222 ("clean", "make clean", "text/plain"),
223 ("ctdb-clean", "cd ./ctdb && make clean", "text/plain")],
226 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
227 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_libs
, "text/plain"),
228 ("talloc-make", "cd lib/talloc && make", "text/plain"),
229 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
231 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_libs
, "text/plain"),
232 ("tdb-make", "cd lib/tdb && make", "text/plain"),
233 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
235 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_libs
, "text/plain"),
236 ("tevent-make", "cd lib/tevent && make", "text/plain"),
237 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
239 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_libs
, "text/plain"),
240 ("ldb-make", "cd lib/ldb && make", "text/plain"),
241 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
243 ("nondevel-configure", "./configure ${PREFIX}", "text/plain"),
244 ("nondevel-make", "make -j", "text/plain"),
245 ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0", "text/plain"),
246 ("nondevel-install", "make install", "text/plain"),
247 ("nondevel-dist", "make dist", "text/plain"),
249 # retry with all modules shared
250 ("allshared-distclean", "make distclean", "text/plain"),
251 ("allshared-configure", samba_libs_configure_samba
+ " --with-shared-modules=ALL", "text/plain"),
252 ("allshared-make", "make -j", "text/plain")],
255 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
256 ("configure", "./configure.developer --with-selftest-prefix=./bin/ab" + samba_configure_params
, "text/plain"),
257 ("make", "make -j", "text/plain"),
258 ("test", "make test "
259 "FAIL_IMMEDIATELY=1 "
261 "--include-env=none'",
265 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
266 # build with all modules static
267 ("allstatic-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=ALL", "text/plain"),
268 ("allstatic-make", "make -j", "text/plain"),
269 ("allstatic-test", "make test "
270 "FAIL_IMMEDIATELY=1 "
271 "TESTS='samba3.smb2.create.*nt4_dc'",
274 # retry without any required modules
275 ("none-distclean", "make distclean", "text/plain"),
276 ("none-configure", "./configure.developer " + samba_configure_params
+ " --with-static-modules=!FORCED,!DEFAULT --with-shared-modules=!FORCED,!DEFAULT", "text/plain"),
277 ("none-make", "make -j", "text/plain"),
279 # retry with nonshared smbd and smbtorture
280 ("nonshared-distclean", "make distclean", "text/plain"),
281 ("nonshared-configure", "./configure.developer " + samba_configure_params
+ " --bundled-libraries=talloc,tdb,pytdb,ldb,pyldb,tevent,pytevent --with-static-modules=ALL --nonshared-binary=smbtorture,smbd/smbd", "text/plain"),
282 ("nonshared-make", "make -j", "text/plain")],
284 "samba-systemkrb5": [
285 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
286 ("configure", "./configure.developer " + samba_configure_params
+ " --with-system-mitkrb5 --without-ad-dc", "text/plain"),
287 ("make", "make -j", "text/plain"),
288 # we currently cannot run a full make test, a limited list of tests could be run
289 # via "make test TESTS=sometests"
290 ("test", "make test FAIL_IMMEDIATELY=1 "
292 "--include-env=ktest'", "text/plain"),
293 ("install", "make install", "text/plain"),
294 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
295 ("clean", "make clean", "text/plain")
298 # Test Samba without python still builds. When this test fails
299 # due to more use of Python, the expectations is that the newly
300 # failing part of the code should be disabled when
301 # --disable-python is set (rather than major work being done to
302 # support this environment). The target here is for vendors
303 # shipping a minimal smbd.
305 ("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
306 ("configure", "./configure.developer --picky-developer ${PREFIX} --with-profiling-data --disable-python --without-ad-dc", "text/plain"),
307 ("make", "make -j", "text/plain"),
308 ("install", "make install", "text/plain"),
309 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
310 ("clean", "make clean", "text/plain"),
312 ("talloc-configure", "cd lib/talloc && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
313 ("talloc-make", "cd lib/talloc && make", "text/plain"),
314 ("talloc-install", "cd lib/talloc && make install", "text/plain"),
316 ("tdb-configure", "cd lib/tdb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
317 ("tdb-make", "cd lib/tdb && make", "text/plain"),
318 ("tdb-install", "cd lib/tdb && make install", "text/plain"),
320 ("tevent-configure", "cd lib/tevent && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
321 ("tevent-make", "cd lib/tevent && make", "text/plain"),
322 ("tevent-install", "cd lib/tevent && make install", "text/plain"),
324 ("ldb-configure", "cd lib/ldb && " + samba_libs_configure_base
+ " --bundled-libraries=cmocka,NONE --disable-python", "text/plain"),
325 ("ldb-make", "cd lib/ldb && make", "text/plain"),
326 ("ldb-install", "cd lib/ldb && make install", "text/plain"),
328 # retry against installed library packages
329 ("libs-configure", samba_libs_configure_base
+ samba_libs_configure_bundled_libs
+ " --disable-python --without-ad-dc", "text/plain"),
330 ("libs-make", "make -j", "text/plain"),
331 ("libs-install", "make install", "text/plain"),
332 ("libs-check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
333 ("libs-clean", "make clean", "text/plain")
339 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
340 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
341 ("make", "make", "text/plain"),
342 ("install", "make install", "text/plain"),
343 ("test", "make test", "text/plain"),
344 ("configure-no-lmdb", "./configure --enable-developer --without-ldb-lmdb -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
345 ("make-no-lmdb", "make", "text/plain"),
346 ("install-no-lmdb", "make install", "text/plain"),
347 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
348 ("distcheck", "make distcheck", "text/plain"),
349 ("clean", "make clean", "text/plain")],
352 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
353 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
354 ("make", "make", "text/plain"),
355 ("install", "make install", "text/plain"),
356 ("test", "make test", "text/plain"),
357 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
358 ("distcheck", "make distcheck", "text/plain"),
359 ("clean", "make clean", "text/plain")],
362 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
363 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
364 ("make", "make", "text/plain"),
365 ("install", "make install", "text/plain"),
366 ("test", "make test", "text/plain"),
367 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
368 ("distcheck", "make distcheck", "text/plain"),
369 ("clean", "make clean", "text/plain")],
372 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
373 ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
374 ("make", "make", "text/plain"),
375 ("install", "make install", "text/plain"),
376 ("test", "make test", "text/plain"),
377 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
378 ("distcheck", "make distcheck", "text/plain"),
379 ("clean", "make clean", "text/plain")],
382 ("random-sleep", "../../script/random-sleep.sh 60 600", "text/plain"),
383 ("configure", "./configure --enable-developer -C ${PREFIX} ${EXTRA_PYTHON}", "text/plain"),
384 ("make", "make", "text/plain"),
385 ("install", "make install", "text/plain"),
386 ("test", "make test", "text/plain"),
387 ("check-clean-tree", "../../script/clean-source-tree.sh", "text/plain"),
388 ("distcheck", "make distcheck", "text/plain"),
389 ("clean", "make clean", "text/plain")],
392 ("random-sleep", "../script/random-sleep.sh 60 600", "text/plain"),
393 ("configure", "perl Makefile.PL PREFIX=${PREFIX_DIR}", "text/plain"),
394 ("touch", "touch *.yp", "text/plain"),
395 ("make", "make", "text/plain"),
396 ("test", "make test", "text/plain"),
397 ("install", "make install", "text/plain"),
398 ("checkout-yapp-generated", "git checkout lib/Parse/Pidl/IDL.pm lib/Parse/Pidl/Expr.pm", "text/plain"),
399 ("check-clean-tree", "../script/clean-source-tree.sh", "text/plain"),
400 ("clean", "make clean", "text/plain")],
402 # these are useful for debugging autobuild
403 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
404 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
414 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
416 show
= options
.verbose
418 do_print("Running: '%s' in '%s'" % (cmd
, dir))
420 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir, close_fds
=True).communicate()[0]
422 return check_call(cmd
, shell
=True, cwd
=dir)
424 return call(cmd
, shell
=True, cwd
=dir)
427 class builder(object):
428 '''handle build of one directory'''
430 def __init__(self
, name
, sequence
, cp
=True, py3
=False):
433 if name
in builddirs
:
434 self
.dir = builddirs
[name
]
438 self
.tag
= self
.name
.replace('/', '_')
439 self
.sequence
= sequence
441 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
442 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
444 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
445 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
446 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
447 self
.stdout
= open(self
.stdout_path
, 'w')
448 self
.stderr
= open(self
.stderr_path
, 'w')
449 self
.stdin
= open("/dev/null", 'r')
450 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
451 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
452 run_cmd("rm -rf %s" % self
.sdir
)
453 run_cmd("rm -rf %s" % self
.prefix
)
455 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
457 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
460 def start_next(self
):
461 if self
.next
== len(self
.sequence
):
462 if not options
.nocleanup
:
463 run_cmd("rm -rf %s" % self
.sdir
)
464 run_cmd("rm -rf %s" % self
.prefix
)
465 do_print('%s: Completed OK' % self
.name
)
468 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
469 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(standard_lib
=1, prefix
=self
.prefix
))
470 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
472 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
473 # The trailing space is important
474 self
.cmd
= self
.cmd
.replace("${PY3_ONLY}", "python3 ")
476 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "")
477 self
.cmd
= self
.cmd
.replace("${PY3_ONLY}", "")
478 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
479 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
480 # if self.output_mime_type == "text/x-subunit":
481 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
483 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
484 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, os
.getcwd()))
485 self
.proc
= Popen(self
.cmd
, shell
=True,
487 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
492 class buildlist(object):
493 '''handle build of multiple directories'''
495 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
498 self
.tail_proc
= None
501 if options
.restrict_tests
:
502 tasknames
= ["samba-test-only"]
504 tasknames
= defaulttasks
506 # If we are only running one test,
507 # do not sleep randomly to wait for it to start
508 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
511 if n
not in tasks
and n
.endswith("-py3"):
517 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
520 rebase_remote
= "rebaseon"
521 retry_task
= [("retry",
523 git remote add -t %s %s %s
527 git describe %s/%s > old_remote_branch.desc
529 git describe %s/%s > remote_branch.desc
530 diff old_remote_branch.desc remote_branch.desc
533 rebase_branch
, rebase_remote
, rebase_url
,
535 rebase_remote
, rebase_branch
,
537 rebase_remote
, rebase_branch
541 self
.retry
= builder('retry', retry_task
, cp
=False)
542 self
.need_retry
= False
545 if self
.tail_proc
is not None:
546 self
.tail_proc
.terminate()
547 self
.tail_proc
.wait()
548 self
.tail_proc
= None
549 if self
.retry
is not None:
550 self
.retry
.proc
.terminate()
551 self
.retry
.proc
.wait()
554 if b
.proc
is not None:
555 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
567 b
.status
= b
.proc
.poll()
573 ret
= self
.retry
.proc
.poll()
575 self
.need_retry
= True
585 if options
.retry
and self
.need_retry
:
587 do_print("retry needed")
588 return (0, None, None, None, "retry")
591 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
593 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
596 return (0, None, None, None, "All OK")
598 def write_system_info(self
):
599 filename
= 'system-info.txt'
600 f
= open(filename
, 'w')
601 for cmd
in ['uname -a', 'free', 'cat /proc/cpuinfo',
602 'cc --version', 'df -m .', 'df -m %s' % testbase
]:
603 print('### %s' % cmd
, file=f
)
604 print(run_cmd(cmd
, output
=True, checkfail
=False), file=f
)
609 def tarlogs(self
, fname
):
610 tar
= tarfile
.open(fname
, "w:gz")
612 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
613 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
614 if os
.path
.exists("autobuild.log"):
615 tar
.add("autobuild.log")
616 sys_info
= self
.write_system_info()
620 def remove_logs(self
):
622 os
.unlink(b
.stdout_path
)
623 os
.unlink(b
.stderr_path
)
625 def start_tail(self
):
628 cmd
.append(b
.stdout_path
)
629 cmd
.append(b
.stderr_path
)
630 self
.tail_proc
= Popen(cmd
, close_fds
=True)
634 if options
.nocleanup
:
636 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
637 run_cmd("stat %s" % testbase
, show
=True)
638 do_print("Cleaning up %r" % cleanup_list
)
639 for d
in cleanup_list
:
640 run_cmd("rm -rf %s" % d
)
644 '''get to the top of the git repo'''
647 if os
.path
.isdir(os
.path
.join(p
, ".git")):
649 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
653 def daemonize(logfile
):
655 if pid
== 0: # Parent
658 if pid
!= 0: # Actual daemon
663 import resource
# Resource usage information.
664 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
665 if maxfd
== resource
.RLIM_INFINITY
:
666 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
667 for fd
in range(0, maxfd
):
672 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
677 def write_pidfile(fname
):
678 '''write a pid file, cleanup on exit'''
679 f
= open(fname
, mode
='w')
680 f
.write("%u\n" % os
.getpid())
684 def rebase_tree(rebase_url
, rebase_branch
="master"):
685 rebase_remote
= "rebaseon"
686 do_print("Rebasing on %s" % rebase_url
)
687 run_cmd("git describe HEAD", show
=True, dir=test_master
)
688 run_cmd("git remote add -t %s %s %s" %
689 (rebase_branch
, rebase_remote
, rebase_url
),
690 show
=True, dir=test_master
)
691 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
692 if options
.fix_whitespace
:
693 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
694 (rebase_remote
, rebase_branch
),
695 show
=True, dir=test_master
)
697 run_cmd("git rebase --force-rebase %s/%s" %
698 (rebase_remote
, rebase_branch
),
699 show
=True, dir=test_master
)
700 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
701 (rebase_remote
, rebase_branch
),
702 dir=test_master
, output
=True)
704 do_print("No differences between HEAD and %s/%s - exiting" %
705 (rebase_remote
, rebase_branch
))
707 run_cmd("git describe %s/%s" %
708 (rebase_remote
, rebase_branch
),
709 show
=True, dir=test_master
)
710 run_cmd("git describe HEAD", show
=True, dir=test_master
)
711 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
712 (rebase_remote
, rebase_branch
),
713 show
=True, dir=test_master
)
716 def push_to(push_url
, push_branch
="master"):
717 push_remote
= "pushto"
718 do_print("Pushing to %s" % push_url
)
720 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
721 run_cmd("git commit --amend -c HEAD", dir=test_master
)
722 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
723 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
724 run_cmd("git remote add -t %s %s %s" %
725 (push_branch
, push_remote
, push_url
),
726 show
=True, dir=test_master
)
727 run_cmd("git push %s +HEAD:%s" %
728 (push_remote
, push_branch
),
729 show
=True, dir=test_master
)
732 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
734 gitroot
= find_git_root()
736 raise Exception("Failed to find git root")
738 parser
= OptionParser()
739 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
740 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
741 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
742 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
743 default
=def_testbase
)
744 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
745 parser
.add_option("", "--verbose", help="show all commands as they are run",
746 default
=False, action
="store_true")
747 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
748 default
=None, type='str')
749 parser
.add_option("", "--pushto", help="push to a git url on success",
750 default
=None, type='str')
751 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
752 default
=False, action
="store_true")
753 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
754 default
=False, action
="store_true")
755 parser
.add_option("", "--retry", help="automatically retry if master changes",
756 default
=False, action
="store_true")
757 parser
.add_option("", "--email", help="send email to the given address on failure",
758 type='str', default
=None)
759 parser
.add_option("", "--email-from", help="send email from the given address",
760 type='str', default
="autobuild@samba.org")
761 parser
.add_option("", "--email-server", help="send email via the given server",
762 type='str', default
='localhost')
763 parser
.add_option("", "--always-email", help="always send email, even on success",
765 parser
.add_option("", "--daemon", help="daemonize after initial setup",
767 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
768 default
="master", type='str')
769 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
770 default
=gitroot
, type='str')
771 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
772 default
=False, action
="store_true")
773 parser
.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
777 def send_email(subject
, text
, log_tar
):
778 if options
.email
is None:
779 do_print("not sending email because the recipient is not set")
780 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
783 outer
= MIMEMultipart()
784 outer
['Subject'] = subject
785 outer
['To'] = options
.email
786 outer
['From'] = options
.email_from
787 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
788 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
789 outer
.attach(MIMEText(text
, 'plain'))
790 if options
.attach_logs
:
791 fp
= open(log_tar
, 'rb')
792 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
794 # Set the filename parameter
795 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
797 content
= outer
.as_string()
798 s
= smtplib
.SMTP(options
.email_server
)
799 s
.sendmail(options
.email_from
, [options
.email
], content
)
804 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
805 elapsed_time
, log_base
=None, add_log_tail
=True):
806 '''send an email to options.email about the failure'''
807 elapsed_minutes
= elapsed_time
/ 60.0
808 user
= os
.getenv("USER")
814 Your autobuild on %s failed after %.1f minutes
815 when trying to test %s with the following error:
819 the autobuild has been abandoned. Please fix the error and resubmit.
821 A summary of the autobuild process is here:
824 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
826 if options
.restrict_tests
:
828 The build was restricted to tests matching %s\n""" % options
.restrict_tests
830 if failed_task
!= 'rebase':
832 You can see logs of the failed task here:
837 or you can get full logs of all tasks in this job here:
841 The top commit for the tree that was built was:
845 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
848 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
849 lines
= f
.readlines()
850 log_tail
= "".join(lines
[-50:])
851 num_lines
= len(lines
)
853 # Also include stderr (compile failures) if < 50 lines of stdout
854 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
855 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
858 The last 50 lines of log messages:
864 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
865 send_email('autobuild[%s] failure on %s for task %s during %s'
866 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
870 def email_success(elapsed_time
, log_base
=None):
871 '''send an email to options.email about a successful build'''
872 user
= os
.getenv("USER")
878 Your autobuild on %s has succeeded after %.1f minutes.
880 ''' % (platform
.node(), elapsed_time
/ 60.)
882 if options
.restrict_tests
:
884 The build was restricted to tests matching %s\n""" % options
.restrict_tests
889 you can get full logs of all tasks in this job here:
896 The top commit for the tree that was built was:
901 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
902 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
906 (options
, args
) = parser
.parse_args()
909 if options
.rebase
is None:
910 raise Exception('You can only use --retry if you also rebase')
912 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
913 test_master
= "%s/master" % testbase
914 test_prefix
= "%s/prefix" % testbase
915 test_tmpdir
= "%s/tmp" % testbase
916 os
.environ
['TMPDIR'] = test_tmpdir
918 # get the top commit message, for emails
919 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
922 os
.makedirs(testbase
)
923 except Exception as reason
:
924 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
925 cleanup_list
.append(testbase
)
928 logfile
= os
.path
.join(testbase
, "log")
929 do_print("Forking into the background, writing progress to %s" % logfile
)
932 write_pidfile(gitroot
+ "/autobuild.pid")
934 start_time
= time
.time()
938 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
939 os
.makedirs(test_tmpdir
)
940 # The waf uninstall code removes empty directories all the way
941 # up the tree. Creating a file in test_tmpdir stops it from
943 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
944 ".directory-is-not-empty"), show
=True)
945 run_cmd("stat %s" % test_tmpdir
, show
=True)
946 run_cmd("stat %s" % testbase
, show
=True)
947 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
954 if options
.rebase
is not None:
955 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
957 cleanup_list
.append(gitroot
+ "/autobuild.pid")
959 elapsed_time
= time
.time() - start_time
960 email_failure(-1, 'rebase', 'rebase', 'rebase',
961 'rebase on %s failed' % options
.branch
,
962 elapsed_time
, log_base
=options
.log_base
)
964 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
967 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
968 if status
!= 0 or errstr
!= "retry":
975 cleanup_list
.append(gitroot
+ "/autobuild.pid")
981 do_print("waiting for tail to flush")
984 elapsed_time
= time
.time() - start_time
986 if options
.passcmd
is not None:
987 do_print("Running passcmd: %s" % options
.passcmd
)
988 run_cmd(options
.passcmd
, dir=test_master
)
989 if options
.pushto
is not None:
990 push_to(options
.pushto
, push_branch
=options
.branch
)
991 if options
.keeplogs
or options
.attach_logs
:
992 blist
.tarlogs("logs.tar.gz")
993 do_print("Logs in logs.tar.gz")
994 if options
.always_email
:
995 email_success(elapsed_time
, log_base
=options
.log_base
)
1001 # something failed, gather a tar of the logs
1002 blist
.tarlogs("logs.tar.gz")
1004 if options
.email
is not None:
1005 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1006 elapsed_time
, log_base
=options
.log_base
)
1008 elapsed_minutes
= elapsed_time
/ 60.0
1011 ####################################################################
1015 Your autobuild[%s] on %s failed after %.1f minutes
1016 when trying to test %s with the following error:
1020 the autobuild has been abandoned. Please fix the error and resubmit.
1022 ####################################################################
1024 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1028 do_print("Logs in logs.tar.gz")