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-py2": ".",
49 "samba-none-env": ".",
51 "samba-ad-dc-py2": ".",
53 "samba-ad-dc-2-py2": ".",
54 "samba-systemkrb5": ".",
55 "samba-nopython": ".",
56 "samba-buildpy2-only": ".",
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}:$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/python2"
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 --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 -Wp,-D_FORTIFY_SOURCE=2' ./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}:$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}:$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 "samba-buildpy2-only": [("random-sleep", "script/random-sleep.sh 60 600", "text/plain"),
403 ("configure", "PYTHON='python' ./configure.developer --with-selftest-prefix=./bin/ab " + samba_configure_params
, "text/plain"),
404 ("make", "PYTHON='python' make -j", "text/plain"),
405 ("install", "PYTHON='python' make install", "text/plain"),
406 ("check-clean-tree", "script/clean-source-tree.sh", "text/plain"),
407 ("clean", "PYTHON='python' make clean", "text/plain")],
410 # these are useful for debugging autobuild
411 'pass': [("pass", 'echo passing && /bin/true', "text/plain")],
412 'fail': [("fail", 'echo failing && /bin/false', "text/plain")]
424 def run_cmd(cmd
, dir=".", show
=None, output
=False, checkfail
=True):
426 show
= options
.verbose
428 do_print("Running: '%s' in '%s'" % (cmd
, dir))
430 return Popen([cmd
], shell
=True, stdout
=PIPE
, cwd
=dir, close_fds
=True).communicate()[0]
432 return check_call(cmd
, shell
=True, cwd
=dir)
434 return call(cmd
, shell
=True, cwd
=dir)
437 class builder(object):
438 '''handle build of one directory'''
440 def __init__(self
, name
, sequence
, cp
=True, py3
=False):
443 if name
in builddirs
:
444 self
.dir = builddirs
[name
]
448 self
.tag
= self
.name
.replace('/', '_')
449 self
.sequence
= sequence
451 self
.stdout_path
= "%s/%s.stdout" % (gitroot
, self
.tag
)
452 self
.stderr_path
= "%s/%s.stderr" % (gitroot
, self
.tag
)
454 do_print("stdout for %s in %s" % (self
.name
, self
.stdout_path
))
455 do_print("stderr for %s in %s" % (self
.name
, self
.stderr_path
))
456 run_cmd("rm -f %s %s" % (self
.stdout_path
, self
.stderr_path
))
457 self
.stdout
= open(self
.stdout_path
, 'w')
458 self
.stderr
= open(self
.stderr_path
, 'w')
459 self
.stdin
= open("/dev/null", 'r')
460 self
.sdir
= "%s/%s" % (testbase
, self
.tag
)
461 self
.prefix
= "%s/%s" % (test_prefix
, self
.tag
)
462 run_cmd("rm -rf %s" % self
.sdir
)
463 run_cmd("rm -rf %s" % self
.prefix
)
465 run_cmd("cp --recursive --link --archive %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
467 run_cmd("git clone --recursive --shared %s %s" % (test_master
, self
.sdir
), dir=test_master
, show
=True)
470 def start_next(self
):
471 if self
.next
== len(self
.sequence
):
472 if not options
.nocleanup
:
473 run_cmd("rm -rf %s" % self
.sdir
)
474 run_cmd("rm -rf %s" % self
.prefix
)
475 do_print('%s: Completed OK' % self
.name
)
478 (self
.stage
, self
.cmd
, self
.output_mime_type
) = self
.sequence
[self
.next
]
479 self
.cmd
= self
.cmd
.replace("${PYTHON_PREFIX}", get_python_lib(plat_specific
=1, standard_lib
=0, prefix
=self
.prefix
))
480 self
.cmd
= self
.cmd
.replace("${PREFIX}", "--prefix=%s" % self
.prefix
)
482 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "%s" % extra_python
)
483 # The trailing space is important
484 self
.cmd
= self
.cmd
.replace("${PY3_ONLY}", "python2 ")
486 self
.cmd
= self
.cmd
.replace("${EXTRA_PYTHON}", "")
487 self
.cmd
= self
.cmd
.replace("${PY3_ONLY}", "")
488 self
.cmd
= self
.cmd
.replace("${PREFIX_DIR}", "%s" % self
.prefix
)
489 self
.cmd
= self
.cmd
.replace("${TESTS}", options
.restrict_tests
)
490 # if self.output_mime_type == "text/x-subunit":
491 # self.cmd += " | %s --immediate" % (os.path.join(os.path.dirname(__file__), "selftest/format-subunit"))
493 os
.chdir("%s/%s" % (self
.sdir
, self
.dir))
494 do_print('%s: [%s] Running %s in %r' % (self
.name
, self
.stage
, self
.cmd
, os
.getcwd()))
495 self
.proc
= Popen(self
.cmd
, shell
=True,
497 stdout
=self
.stdout
, stderr
=self
.stderr
, stdin
=self
.stdin
)
502 class buildlist(object):
503 '''handle build of multiple directories'''
505 def __init__(self
, tasknames
, rebase_url
, rebase_branch
="master"):
508 self
.tail_proc
= None
511 if options
.restrict_tests
:
512 tasknames
= ["samba-test-only"]
514 tasknames
= defaulttasks
516 # If we are only running one test,
517 # do not sleep randomly to wait for it to start
518 os
.environ
['AUTOBUILD_RANDOM_SLEEP_OVERRIDE'] = '1'
521 if n
not in tasks
and n
.endswith("-py2"):
527 b
= builder(n
, tasks
[n
], cp
=n
is not "pidl")
530 rebase_remote
= "rebaseon"
531 retry_task
= [("retry",
533 git remote add -t %s %s %s
537 git describe %s/%s > old_remote_branch.desc
539 git describe %s/%s > remote_branch.desc
540 diff old_remote_branch.desc remote_branch.desc
543 rebase_branch
, rebase_remote
, rebase_url
,
545 rebase_remote
, rebase_branch
,
547 rebase_remote
, rebase_branch
551 self
.retry
= builder('retry', retry_task
, cp
=False)
552 self
.need_retry
= False
555 if self
.tail_proc
is not None:
556 self
.tail_proc
.terminate()
557 self
.tail_proc
.wait()
558 self
.tail_proc
= None
559 if self
.retry
is not None:
560 self
.retry
.proc
.terminate()
561 self
.retry
.proc
.wait()
564 if b
.proc
is not None:
565 run_cmd("killbysubdir %s > /dev/null 2>&1" % b
.sdir
, checkfail
=False)
577 b
.status
= b
.proc
.poll()
583 ret
= self
.retry
.proc
.poll()
585 self
.need_retry
= True
595 if options
.retry
and self
.need_retry
:
597 do_print("retry needed")
598 return (0, None, None, None, "retry")
601 if os
.WIFSIGNALED(b
.status
) or os
.WEXITSTATUS(b
.status
) != 0:
603 return (b
.status
, b
.name
, b
.stage
, b
.tag
, "%s: [%s] failed '%s' with status %d" % (b
.name
, b
.stage
, b
.cmd
, b
.status
))
606 return (0, None, None, None, "All OK")
608 def write_system_info(self
):
609 filename
= 'system-info.txt'
610 f
= open(filename
, 'w')
611 for cmd
in ['uname -a',
616 'df -m %s' % testbase
]:
617 out
= run_cmd(cmd
, output
=True, checkfail
=False)
618 print('### %s' % cmd
, file=f
)
619 print(out
.decode('utf8', 'backslashreplace'), file=f
)
624 def tarlogs(self
, fname
):
625 tar
= tarfile
.open(fname
, "w:gz")
627 tar
.add(b
.stdout_path
, arcname
="%s.stdout" % b
.tag
)
628 tar
.add(b
.stderr_path
, arcname
="%s.stderr" % b
.tag
)
629 if os
.path
.exists("autobuild.log"):
630 tar
.add("autobuild.log")
631 sys_info
= self
.write_system_info()
635 def remove_logs(self
):
637 os
.unlink(b
.stdout_path
)
638 os
.unlink(b
.stderr_path
)
640 def start_tail(self
):
643 cmd
.append(b
.stdout_path
)
644 cmd
.append(b
.stderr_path
)
645 self
.tail_proc
= Popen(cmd
, close_fds
=True)
649 if options
.nocleanup
:
651 run_cmd("stat %s || true" % test_tmpdir
, show
=True)
652 run_cmd("stat %s" % testbase
, show
=True)
653 do_print("Cleaning up %r" % cleanup_list
)
654 for d
in cleanup_list
:
655 run_cmd("rm -rf %s" % d
)
659 '''get to the top of the git repo'''
662 if os
.path
.isdir(os
.path
.join(p
, ".git")):
664 p
= os
.path
.abspath(os
.path
.join(p
, '..'))
668 def daemonize(logfile
):
670 if pid
== 0: # Parent
673 if pid
!= 0: # Actual daemon
678 import resource
# Resource usage information.
679 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
680 if maxfd
== resource
.RLIM_INFINITY
:
681 maxfd
= 1024 # Rough guess at maximum number of open file descriptors.
682 for fd
in range(0, maxfd
):
687 os
.open(logfile
, os
.O_RDWR | os
.O_CREAT
)
692 def write_pidfile(fname
):
693 '''write a pid file, cleanup on exit'''
694 f
= open(fname
, mode
='w')
695 f
.write("%u\n" % os
.getpid())
699 def rebase_tree(rebase_url
, rebase_branch
="master"):
700 rebase_remote
= "rebaseon"
701 do_print("Rebasing on %s" % rebase_url
)
702 run_cmd("git describe HEAD", show
=True, dir=test_master
)
703 run_cmd("git remote add -t %s %s %s" %
704 (rebase_branch
, rebase_remote
, rebase_url
),
705 show
=True, dir=test_master
)
706 run_cmd("git fetch %s" % rebase_remote
, show
=True, dir=test_master
)
707 if options
.fix_whitespace
:
708 run_cmd("git rebase --force-rebase --whitespace=fix %s/%s" %
709 (rebase_remote
, rebase_branch
),
710 show
=True, dir=test_master
)
712 run_cmd("git rebase --force-rebase %s/%s" %
713 (rebase_remote
, rebase_branch
),
714 show
=True, dir=test_master
)
715 diff
= run_cmd("git --no-pager diff HEAD %s/%s" %
716 (rebase_remote
, rebase_branch
),
717 dir=test_master
, output
=True)
719 do_print("No differences between HEAD and %s/%s - exiting" %
720 (rebase_remote
, rebase_branch
))
722 run_cmd("git describe %s/%s" %
723 (rebase_remote
, rebase_branch
),
724 show
=True, dir=test_master
)
725 run_cmd("git describe HEAD", show
=True, dir=test_master
)
726 run_cmd("git --no-pager diff --stat HEAD %s/%s" %
727 (rebase_remote
, rebase_branch
),
728 show
=True, dir=test_master
)
731 def push_to(push_url
, push_branch
="master"):
732 push_remote
= "pushto"
733 do_print("Pushing to %s" % push_url
)
735 run_cmd("git config --replace-all core.editor script/commit_mark.sh", dir=test_master
)
736 run_cmd("git commit --amend -c HEAD", dir=test_master
)
737 # the notes method doesn't work yet, as metze hasn't allowed refs/notes/* in master
738 # run_cmd("EDITOR=script/commit_mark.sh git notes edit HEAD", dir=test_master)
739 run_cmd("git remote add -t %s %s %s" %
740 (push_branch
, push_remote
, push_url
),
741 show
=True, dir=test_master
)
742 run_cmd("git push %s +HEAD:%s" %
743 (push_remote
, push_branch
),
744 show
=True, dir=test_master
)
747 def_testbase
= os
.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os
.getenv('USER'))
749 gitroot
= find_git_root()
751 raise Exception("Failed to find git root")
753 parser
= OptionParser()
754 parser
.add_option("", "--tail", help="show output while running", default
=False, action
="store_true")
755 parser
.add_option("", "--keeplogs", help="keep logs", default
=False, action
="store_true")
756 parser
.add_option("", "--nocleanup", help="don't remove test tree", default
=False, action
="store_true")
757 parser
.add_option("", "--testbase", help="base directory to run tests in (default %s)" % def_testbase
,
758 default
=def_testbase
)
759 parser
.add_option("", "--passcmd", help="command to run on success", default
=None)
760 parser
.add_option("", "--verbose", help="show all commands as they are run",
761 default
=False, action
="store_true")
762 parser
.add_option("", "--rebase", help="rebase on the given tree before testing",
763 default
=None, type='str')
764 parser
.add_option("", "--pushto", help="push to a git url on success",
765 default
=None, type='str')
766 parser
.add_option("", "--mark", help="add a Tested-By signoff before pushing",
767 default
=False, action
="store_true")
768 parser
.add_option("", "--fix-whitespace", help="fix whitespace on rebase",
769 default
=False, action
="store_true")
770 parser
.add_option("", "--retry", help="automatically retry if master changes",
771 default
=False, action
="store_true")
772 parser
.add_option("", "--email", help="send email to the given address on failure",
773 type='str', default
=None)
774 parser
.add_option("", "--email-from", help="send email from the given address",
775 type='str', default
="autobuild@samba.org")
776 parser
.add_option("", "--email-server", help="send email via the given server",
777 type='str', default
='localhost')
778 parser
.add_option("", "--always-email", help="always send email, even on success",
780 parser
.add_option("", "--daemon", help="daemonize after initial setup",
782 parser
.add_option("", "--branch", help="the branch to work on (default=master)",
783 default
="master", type='str')
784 parser
.add_option("", "--log-base", help="location where the logs can be found (default=cwd)",
785 default
=gitroot
, type='str')
786 parser
.add_option("", "--attach-logs", help="Attach logs to mails sent on success/failure?",
787 default
=False, action
="store_true")
788 parser
.add_option("", "--restrict-tests", help="run as make test with this TESTS= regex",
792 def send_email(subject
, text
, log_tar
):
793 if options
.email
is None:
794 do_print("not sending email because the recipient is not set")
795 do_print("the text content would have been:\n\nSubject: %s\n\n%s" %
798 outer
= MIMEMultipart()
799 outer
['Subject'] = subject
800 outer
['To'] = options
.email
801 outer
['From'] = options
.email_from
802 outer
['Date'] = email
.utils
.formatdate(localtime
=True)
803 outer
.preamble
= 'Autobuild mails are now in MIME because we optionally attach the logs.\n'
804 outer
.attach(MIMEText(text
, 'plain'))
805 if options
.attach_logs
:
806 fp
= open(log_tar
, 'rb')
807 msg
= MIMEApplication(fp
.read(), 'gzip', email
.encoders
.encode_base64
)
809 # Set the filename parameter
810 msg
.add_header('Content-Disposition', 'attachment', filename
=os
.path
.basename(log_tar
))
812 content
= outer
.as_string()
813 s
= smtplib
.SMTP(options
.email_server
)
814 s
.sendmail(options
.email_from
, [options
.email
], content
)
819 def email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
820 elapsed_time
, log_base
=None, add_log_tail
=True):
821 '''send an email to options.email about the failure'''
822 elapsed_minutes
= elapsed_time
/ 60.0
828 Your autobuild on %s failed after %.1f minutes
829 when trying to test %s with the following error:
833 the autobuild has been abandoned. Please fix the error and resubmit.
835 A summary of the autobuild process is here:
838 ''' % (platform
.node(), elapsed_minutes
, failed_task
, errstr
, log_base
)
840 if options
.restrict_tests
:
842 The build was restricted to tests matching %s\n""" % options
.restrict_tests
844 if failed_task
!= 'rebase':
846 You can see logs of the failed task here:
851 or you can get full logs of all tasks in this job here:
855 The top commit for the tree that was built was:
859 ''' % (log_base
, failed_tag
, log_base
, failed_tag
, log_base
, top_commit_msg
)
862 f
= open("%s/%s.stdout" % (gitroot
, failed_tag
), 'r')
863 lines
= f
.readlines()
864 log_tail
= "".join(lines
[-50:])
865 num_lines
= len(lines
)
867 # Also include stderr (compile failures) if < 50 lines of stdout
868 f
= open("%s/%s.stderr" % (gitroot
, failed_tag
), 'r')
869 log_tail
+= "".join(f
.readlines()[-(50 - num_lines
):])
872 The last 50 lines of log messages:
878 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
879 send_email('autobuild[%s] failure on %s for task %s during %s'
880 % (options
.branch
, platform
.node(), failed_task
, failed_stage
),
884 def email_success(elapsed_time
, log_base
=None):
885 '''send an email to options.email about a successful build'''
891 Your autobuild on %s has succeeded after %.1f minutes.
893 ''' % (platform
.node(), elapsed_time
/ 60.)
895 if options
.restrict_tests
:
897 The build was restricted to tests matching %s\n""" % options
.restrict_tests
902 you can get full logs of all tasks in this job here:
909 The top commit for the tree that was built was:
914 logs
= os
.path
.join(gitroot
, 'logs.tar.gz')
915 send_email('autobuild[%s] success on %s' % (options
.branch
, platform
.node()),
919 (options
, args
) = parser
.parse_args()
922 if options
.rebase
is None:
923 raise Exception('You can only use --retry if you also rebase')
925 testbase
= "%s/b%u" % (options
.testbase
, os
.getpid())
926 test_master
= "%s/master" % testbase
927 test_prefix
= "%s/prefix" % testbase
928 test_tmpdir
= "%s/tmp" % testbase
929 os
.environ
['TMPDIR'] = test_tmpdir
931 # get the top commit message, for emails
932 top_commit_msg
= run_cmd("git log -1", dir=gitroot
, output
=True)
933 top_commit_msg
= top_commit_msg
.decode('utf-8', 'backslashreplace')
936 os
.makedirs(testbase
)
937 except Exception as reason
:
938 raise Exception("Unable to create %s : %s" % (testbase
, reason
))
939 cleanup_list
.append(testbase
)
942 logfile
= os
.path
.join(testbase
, "log")
943 do_print("Forking into the background, writing progress to %s" % logfile
)
946 write_pidfile(gitroot
+ "/autobuild.pid")
948 start_time
= time
.time()
952 run_cmd("rm -rf %s" % test_tmpdir
, show
=True)
953 os
.makedirs(test_tmpdir
)
954 # The waf uninstall code removes empty directories all the way
955 # up the tree. Creating a file in test_tmpdir stops it from
957 run_cmd("touch %s" % os
.path
.join(test_tmpdir
,
958 ".directory-is-not-empty"), show
=True)
959 run_cmd("stat %s" % test_tmpdir
, show
=True)
960 run_cmd("stat %s" % testbase
, show
=True)
961 run_cmd("git clone --recursive --shared %s %s" % (gitroot
, test_master
), show
=True, dir=gitroot
)
968 if options
.rebase
is not None:
969 rebase_tree(options
.rebase
, rebase_branch
=options
.branch
)
971 cleanup_list
.append(gitroot
+ "/autobuild.pid")
973 elapsed_time
= time
.time() - start_time
974 email_failure(-1, 'rebase', 'rebase', 'rebase',
975 'rebase on %s failed' % options
.branch
,
976 elapsed_time
, log_base
=options
.log_base
)
978 blist
= buildlist(args
, options
.rebase
, rebase_branch
=options
.branch
)
981 (status
, failed_task
, failed_stage
, failed_tag
, errstr
) = blist
.run()
982 if status
!= 0 or errstr
!= "retry":
989 cleanup_list
.append(gitroot
+ "/autobuild.pid")
995 do_print("waiting for tail to flush")
998 elapsed_time
= time
.time() - start_time
1000 if options
.passcmd
is not None:
1001 do_print("Running passcmd: %s" % options
.passcmd
)
1002 run_cmd(options
.passcmd
, dir=test_master
)
1003 if options
.pushto
is not None:
1004 push_to(options
.pushto
, push_branch
=options
.branch
)
1005 if options
.keeplogs
or options
.attach_logs
:
1006 blist
.tarlogs("logs.tar.gz")
1007 do_print("Logs in logs.tar.gz")
1008 if options
.always_email
:
1009 email_success(elapsed_time
, log_base
=options
.log_base
)
1015 # something failed, gather a tar of the logs
1016 blist
.tarlogs("logs.tar.gz")
1018 if options
.email
is not None:
1019 email_failure(status
, failed_task
, failed_stage
, failed_tag
, errstr
,
1020 elapsed_time
, log_base
=options
.log_base
)
1022 elapsed_minutes
= elapsed_time
/ 60.0
1025 ####################################################################
1029 Your autobuild[%s] on %s failed after %.1f minutes
1030 when trying to test %s with the following error:
1034 the autobuild has been abandoned. Please fix the error and resubmit.
1036 ####################################################################
1038 ''' % (options
.branch
, platform
.node(), elapsed_minutes
, failed_task
, errstr
))
1042 do_print("Logs in logs.tar.gz")