3 # adt-run is part of autopkgtest
4 # autopkgtest is a tool for testing Debian binary packages
6 # autopkgtest is Copyright (C) 2006 Canonical Ltd.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 # See the file CREDITS for a full list of credits information (often
23 # installed as /usr/share/doc/autopkgtest/CREDITS).
39 from optparse import OptionParser
40 signal.signal(signal.SIGINT, signal.SIG_DFL) # undo stupid Python SIGINT thing
42 #---------- global variables
44 tmpdir = None # pathstring on host
45 testbed = None # Testbed
46 errorcode = 0 # exit status that we are going to use
47 binaries = None # Binaries (.debs we have registered)
49 #---------- errors we define
52 def __init__(q,ec,m): q.ec = ec; q.m = m
54 def bomb(m): raise Quit(20, "unexpected error: %s" % m)
56 print 'blame: ', ' '.join(testbed.blamed)
57 raise Quit(12, "erroneous package: %s" % m)
59 def report(tname, result): print '%-20s %s' % (tname, result)
62 def __init__(u, lno, m):
63 if lno >= 0: u.m = '%s (control line %d)' % (m, lno)
68 report(tname, 'SKIP %s' % u.m)
72 if not opts.debug: return
73 print >>sys.stderr, 'atd-run: debug:', m
76 return reduce((lambda a,b: a + b), l, [])
78 #---------- fancy automatic file-copying class
81 # p.path[tb] None or path not None => path known
82 # p.file[tb] None or path not None => file exists
88 def ensure_path(p, tb=False):
89 if tb and not p.spec_tb:
90 if not testbed.scratch:
91 error "called ensure_path for `%s' when testbed closed"
93 if not p.tb_scratch or p.tb_scratch is not testbed.scratch:
96 if p.path[tb] is not None: return
97 if tb: p.path[tb] = p.tb_tmpdir
98 else: p.path[tb] = tmpdir
99 p.path[tb] += '/'+p.what
101 def ensure_file(p, tb=False):
102 if p.file[tb] is not None: return
107 def write(p, tb=False):
110 def read(p, tb=False):
117 def __init__(p, path, spec_tb, what, dir=False):
120 bomb("path %s specified as being in testbed but"
121 " not absolute: `%s'" % (what, p.p))
122 p.path[spec_tb] = p.file[spec_tb] = path
133 4 def __init__(p, tb, path, what, dir=False, tbscratch=None, xfmap=None
139 p.tbscratch = tbscratch
143 bomb("path %s specified as being in testbed but"
144 " not absolute: `%s'" % (what, p.p))
150 if p.dir: p.dirsfx = '/'
153 return p.p + p.dirsfx
154 def append(p, suffix, what, dir=False):
155 return Path(p.tb, p.path() + suffix, what=what, dir=dir,
156 tbscratch=p.tbscratch)
158 if p.tb: pfx = '/VIRT'
159 elif p.p[:1] == '/': pfx = '/HOST'
163 def xfmapcopy(p, cud, dstdir):
164 if p.xfmap is None: return
165 srcdir = os.path.dirname(p.path()+'/')
166 dstdir = p.xfmapdstdir+'/'
167 for f in p.xfmap(file(p.local)):
168 if '/' in f: bomb("control file %s mentions other filename"
169 "containing slash" % p.what)
170 testbed.command(cud, (srcdir+f, dstdir+f))
172 def onhost(p, lpath = None):
173 if lpath is not None:
174 if p.lpath is not None: assert(p.lpath == lpath)
176 if p.local is not None:
177 if p.lpath is not None: assert(p.local == p.lpath)
182 if p.local is None: p.local = tmpdir + '/tb-' + p.what
184 assert(p.lpath is None)
186 p.xfmapdstdir = tmpdir + '/tbd-' + p.what
187 os.mkdir(p.xfmapdstdir)
188 p.local = p.xfmapdstdir + '/' + os.path.basename(p.down)
190 testbed.command('copyup', (p.path(), p.local + p.dirsfx))
191 p.xfmapcopy('copyup')
196 if p.lpath is None: return None
201 if p.tbscratch is not None:
202 if p.tbscratch != testbed.scratch:
204 if p.down is not None: return p.down
206 bomb("testbed scratch path " + str(p) + " survived testbed")
209 p.down = testbed.scratch.p + '/host-' + p.what
212 p.xfmapdstdir = testbed.scratch.p + '/hostd-' + p.what
213 testbed.command('mkdir '+p.xfmapdstdir)
214 p.down = p.xfmapdstdir + '/' + os.path.basename(p.local)
216 p.tbscratch = testbed.scratch
217 testbed.command('copydown', (p.path(), p.down + p.dirsfx))
218 p.xfmapcopy('copydown')
221 #---------- parsing and representation of the arguments
224 def __init__(a, kind, path, arghandling, ix):
225 # extra attributes get added during processing
227 a.path = path # just a string
229 a.what = '%s%s' % (kind,ix)
233 usage = "%prog <options> -- <virt-server>..."
234 parser = OptionParser(usage=usage)
235 pa = parser.add_option
236 pe = parser.add_option
241 'deb_forbuilds': 'auto',
242 'deb_fortests': 'auto',
244 'override_control': None
246 initial_arghandling = arghandling.copy()
250 # actions (ie, test sets to run, sources to build, binaries to use):
252 def cb_action(op,optstr,value,parser, long,kindpath,is_act):
253 parser.largs.append((value,kindpath))
256 def pa_action(long, metavar, kindpath, help, is_act=True):
257 pa('','--'+long, action='callback', callback=cb_action,
258 nargs=1, type='string',
259 callback_args=(long,kindpath,is_act), help=help)
261 pa_action('build-tree', 'TREE', '@/',
262 help='run tests from build tree TREE')
264 pa_action('source', 'DSC', '@.dsc',
265 help='build DSC and use its tests and/or'
266 ' generated binary packages')
268 pa_action('binary', 'DEB', '@.deb',
269 help='use binary package DEB according'
270 ' to most recent --binaries-* settings')
272 pa_action('override-control', 'CONTROL', ('control',), is_act=0,
273 help='run tests from control file CONTROL instead,
274 ' (applies to next test suite only)')
277 # argument handling settings (what ways to use action
278 # arguments, and pathname processing):
280 def cb_setah(option, opt_str, value, parser, toset,setval):
281 if type(setval) == list:
282 if not value in setval:
283 parser.error('value for %s option (%s) is not '
284 'one of the permitted values (%s)' %
285 (value, opt_str, setval.join(' ')))
286 elif setval is not None:
289 arghandling[v] = value
290 parser.largs.append(arghandling.copy())
292 def pa_setah(long, affected,effect, **kwargs):
293 type = metavar; if type: type = 'string'
294 pa('',long, action='callback', callback=cb_setah,
295 callback_args=(affected,effect), **kwargs)
296 ' according to most recent --binaries-* settings')
298 #---- paths: host or testbed:
300 pa_setah('--paths-testbed', ['tb'],True,
301 help='subsequent path specifications refer to the testbed')
302 pa_setah('--paths-host', ['tb'],False,
303 help='subsequent path specifications refer to the host')
305 #---- source processing settings:
307 pa_setah('--sources-tests', ['dsc_tests'],True,
308 help='run tests from builds of subsequent sources')
309 pa_setah('--sources-no-tests', ['dsc_tests'],False,
310 help='do not run tests from builds of subsequent sources')
312 pa_setah('--built-binaries-filter', ['dsc_filter'],None,
313 type=string, metavar='PATTERN-LIST',
314 help='from subsequent sources, use binaries matching'
315 ' PATTERN-LIST (comma-separated glob patterns)'
316 ' according to most recent --binaries-* settings')
317 pa_setah('--no-built-binaries', ['dsc_filter'], '_',
318 help='from subsequent sources, do not use any binaries')
320 #---- binary package processing settings:
322 def pa_setahbins(long,toset,how):
323 pa_setah(long, toset,['ignore','auto','install'],
324 type=string, metavar='IGNORE|AUTO|INSTALL', default='auto',
325 help=how+' ignore binaries, install them as needed'
326 ' for dependencies, or unconditionally install'
327 ' them, respectively')
328 pa_setahbins('--binaries', ['deb_forbuilds','deb_fortests'], '')
329 pa_setahbins('--binaries-forbuilds', ['deb_forbuilds'], 'for builds, ')
330 pa_setahbins('--binaries-fortests', ['deb_fortests'], 'for tests, ')
335 def cb_vserv(op,optstr,value,parser):
336 parser.values.vserver = list(parser.rargs)
339 def cb_path(op,optstr,value,parser, long,dir,xfmap):
340 name = long.replace('-','_')
341 path = Path(arghandling['tb'], value, long, dir, xfmap=xfmap)
342 setattr(parser.values, name, path)
344 def pa_path(long, help, dir=False, xfmap=None):
345 pa('','--'+long, action='callback', callback=cb_path,
346 nargs=1, type='string', callback_args=(long,dir,xfmap),
347 help=, metavar='PATH')
349 pa_path('output-dir', 'write stderr/out files in PATH', dir=True)
351 pa('','--user', type='string', dest='user',
352 help='run tests as USER (needs root on testbed)')
353 pa('','--fakeroot', type='string', dest='fakeroot',
354 help='prefix debian/rules build with FAKEROOT')
355 pa('-d', '--debug', action='store_true', dest='debug');
356 pa('','--gnupg-home', type='string', dest='gnupghome',
357 default='~/.autopkgtest/gpg',
358 help='use GNUPGHOME rather than ~/.autopkgtest (for
359 " signing private apt archive);"
360 " `fresh' means generate new key each time.")
365 class SpecialOption(optparse.Option): pass
366 vs_op = SpecialOption('','--VSERVER-DUMMY')
367 vs_op.action = 'callback'
371 vs_op.callback = cb_vserv
372 vs_op.callback_args = ( )
373 vs_op.callback_kwargs = { }
374 vs_op.help = 'introduces virtualisation server and args'
375 vs_op._short_opts = []
376 vs_op._long_opts = ['---']
380 (opts,args) = parser.parse_args()
381 if not hasattr(opts,'vserver'):
382 parser.error('you must specifiy --- <virt-server>...')
384 parser.error('nothing to do specified')
386 arghandling = initial_arghandling
390 if type(act) == dict:
393 elif type(act) == tuple:
395 elif type(act) == string:
398 error "unknown action in list `%s' having"
399 "type `%s' % (act, type(act))
400 (pathname, kindpath) = act
402 if type(kindpath) is tuple: kind = kindpath[0]
403 elif kindpath.endswith('/'): kind = 'tree'
404 elif kindpath.endswith('.deb'): kind = 'deb'
405 elif kindpath.endswith('.dsc'): kind = 'dsc'
406 else: parser.error("do not know how to handle filename \`%s';"
407 " specify --source --binary or --build-tree")
409 path = InputPath(pathname, arghandling['tb'])
411 opts.actions.append(Action(kind, path, arghandling, ix))
414 def finalise_options():
417 if opts.user is None and 'root-on-testbed' not in caps:
420 if opts.user is None:
421 su = 'suggested-normal-user='
428 print >>sys.stderr, "warning: virtualisation"
429 " system offers several suggested-normal-user"
430 " values: "+('/'.join(ul))+", using "+ul[0]
437 if 'root-on-testbed' not in caps:
438 print >>sys.stderr, "warning: virtualisation"
439 " system does not offer root on testbed,"
440 " but --user option specified: failure likely"
441 opts.user_wrap = lambda x: 'su %s -c "%s"' % (opts.user, x)
443 opts.user_wrap = lambda x: x
445 if opts.fakeroot is None:
448 'root-on-testbed' not in testbed.caps:
449 opts.fakeroot = 'fakeroot'
451 if opts.gnupghome.startswith('~/'):
452 try: home = os.environ['HOME']
454 parser.error("HOME environment variable"
455 " not set, needed for --gnupghome=`%s"
457 opts.gnupghome = home + opts.gnupghome[1:]
458 elif opts.gnupghome == 'fresh':
459 opts.gnupghome = None
461 #---------- testbed management - the Testbed class
472 tb.sp = subprocess.Popen(opts.vserver,
473 stdin=p, stdout=p, stderr=None)
475 tb.caps = tb.command('capabilities')
478 if tb.sp is None: return
479 ec = tb.sp.returncode
486 tb.bomb('testbed gave exit status %d after quit' % ec)
488 if tb.scratch is not None: return
489 p = tb.commandr1('open')
490 tb.scratch = Path(True, p, 'tb-scratch', dir=True)
491 tb.scratch.tbscratch = tb.scratch
493 if tb.scratch is None: return
495 if tb.sp is None: return
498 if tb.modified and 'reset' in caps:
502 binaries.publish(act)
508 if tb.sp is not None:
512 if ec: print >>sys.stderr, ('adt-run: testbed failing,'
513 ' exit status %d' % ec)
515 raise Quit(16, 'testbed failed: %s' % m)
516 def send(tb, string):
520 print >>tb.sp.stdin, string
524 (type, value, dummy) = sys.exc_info()
525 tb.bomb('cannot send to testbed: %s' % traceback.
526 format_exception_only(type, value))
527 def expect(tb, keyword, nresults=-1):
528 l = tb.sp.stdout.readline()
529 if not l: tb.bomb('unexpected eof from the testbed')
530 if not l.endswith('\n'): tb.bomb('unterminated line from the testbed')
534 if not ll: tb.bomb('unexpected whitespace-only line from the testbed')
536 if tb.lastsend is None:
537 tb.bomb("got banner `%s', expected `%s...'" %
540 tb.bomb("sent `%s', got `%s', expected `%s...'" %
541 (tb.lastsend, l, keyword))
543 if nresults >= 0 and len(ll) != nresults:
544 tb.bomb("sent `%s', got `%s' (%d result parameters),"
545 " expected %d result parameters" %
546 (string, l, len(ll), nresults))
548 def commandr(tb, cmd, nresults, args=()):
549 if type(cmd) is str: cmd = [cmd]
550 al = cmd + map(urllib.quote, args)
551 tb.send(string.join(al))
553 rl = map(urllib.unquote, ll)
555 def command(tb, cmd, args=()):
556 tb.commandr(cmd, 0, args)
557 def commandr1(tb, cmd, args=()):
558 rl = tb.commandr(cmd, 1, args)
561 #---------- representation of test control files: Field*, Test, etc.
564 def __init__(f, fname, stz, base, tnames, vl):
574 r = map((lambda w: (lno, w)), r)
576 return flatten(map(distribute, f.vl))
581 raise Unsupported(f.vl[1][0],
582 'only one %s field allowed' % fn)
585 class FieldIgnore(FieldBase):
589 def __init__(r,rname,base): pass
591 class Restriction_rw_tests_tree(Restriction): pass
592 class Restriction_breaks_testbed(Restriction):
593 if 'reset' not in caps:
594 raise Unsupported(f.lno,
595 'Test breaks testbed but testbed cannot reset')
597 class Field_Restrictions(FieldBase):
599 for wle in f.words():
601 rname = rname.replace('-','_')
602 try: rclass = globals()['Restriction_'+rname]
603 except KeyError: raise Unsupported(lno,
604 'unknown restriction %s' % rname)
605 r = rclass(rname, f.base)
606 f.base['restrictions'].append(r)
608 class Field_Tests(FieldIgnore): pass
610 class Field_Tests_directory(FieldBase):
613 if td.startswith('/'): raise Unspported(f.lno,
614 'Tests-Directory may not be absolute')
615 base['testsdir'] = td
617 def run_tests(stanzas):
619 for stanza in stanzas:
620 tests = stanza[' tests']
622 report('*', 'SKIP no tests in this package')
627 if 'breaks-testbed' in t.restrictions:
628 testbed.needs_reset()
629 testbed.needs_reset()
632 def __init__(t, tname, base):
633 if '/' in tname: raise Unsupported(base[' lno'],
634 'test name may not contain / character')
635 for k in base: setattr(t,k,base[k])
637 if len(base['testsdir']): tpath = base['testsdir'] + '/' + tname
639 t.p = opts.tests_tree.append(tpath, 'test-'+tname)
642 def reportfail(t, m):
645 report(t.tname, 'FAIL ' + m)
648 idstr = oe + '-' + t.tname
649 if opts.output_dir is not None and opts.output_dir.tb:
650 return opts.output_dir.append(idstr)
652 return testbed.scratch.append(idstr, idstr)
653 def stdouterrh(p, oe):
654 idstr = oe + '-' + t.tname
655 if opts.output_dir is None or opts.output_dir.tb:
658 return p.onhost(opts.output_dir.onhost() + '/' + idstr)
659 so = stdouterr('stdout')
660 se = stdouterr('stderr')
661 rc = testbed.commandr1('execute',(t.p.ontb(),
662 '/dev/null', so.ontb(), se.ontb(), opts.tests_tree.ontb()))
663 soh = stdouterrh(so, 'stdout')
664 seh = stdouterrh(se, 'stderr')
667 if stab.st_size != 0:
668 l = file(seh).readline()
669 l = l.rstrip('\n \t\r')
670 if len(l) > 40: l = l[:40] + '...'
671 t.reportfail('stderr: %s' % l)
673 t.reportfail('non-zero exit status %d' % rc)
677 def read_control(act, tree, control_override):
680 if control_override is not None:
681 control_path = control_override
682 testbed.blame('arg:'+control_path)
684 control_path = tree.append('/debian/tests/control')
685 testbed.blame('arg:'+tree.spec)
688 control = file(control_path.onhost(), 'r')
690 if oe[0] != errno.ENOENT: raise
694 def badctrl(m): act.bomb('tests/control line %d: %s' % (lno, m))
695 stz = None # stz[field_name][index] = (lno, value)
696 # special field names:
697 # stz[' lno'] = number
698 # stz[' tests'] = list of Test objects
700 if stz is None: return
706 initre = regexp.compile('([A-Z][-0-9a-z]*)\s*\:\s*(.*)$')
708 l = control.readline()
711 if not l.endswith('\n'): badctrl('unterminated line')
712 if regexp.compile('\s*\#').match(l): continue
713 if not regexp.compile('\S').match(l): end_stanza(stz); continue
714 initmat = initre.match(l)
716 (fname, l) = initmat.groups()
717 fname = string.capwords(fname)
719 stz = { ' lno': lno, ' tests': [] }
720 if not stz.has_key(fname): stz[fname] = [ ]
721 hcurrent = stz[fname]
722 elif regexp.compile('\s').match(l):
723 if not hcurrent: badctrl('unexpected continuation')
725 badctrl('syntax error')
726 hcurrent.append((lno, l))
729 def testbadctrl(stz, lno, m):
730 report_badctrl(lno, m)
735 try: tnames = stz['Tests']
738 raise Unsupported(stz[' lno'],
740 tnames = map((lambda lt: lt[1]), tnames)
741 tnames = string.join(tnames).split()
744 'testsdir': 'debian/tests'
746 for fname in stz.keys():
747 if fname.startswith(' '): continue
749 try: fclass = globals()['Field_'+
750 fname.replace('-','_')]
751 except KeyError: raise Unsupported(vl[0][0],
752 'unknown metadata field %s' % fname)
753 f = fclass(stz, fname, base, tnames, vl)
756 t = Test(tname, base)
757 stz[' tests'].append(t)
758 except Unsupported, u:
759 for tname in tnames: u.report(tname)
764 def print_exception(ei, msgprefix=''):
765 if msgprefix: print >>sys.stderr, msgprefix
768 print >>sys.stderr, 'adt-run:', q.m
771 print >>sys.stderr, "adt-run: unexpected, exceptional, error:"
772 traceback.print_exc()
778 if tmpdir is not None:
779 shutil.rmtree(tmpdir)
780 if testbed is not None:
782 if rm_ec: bomb('rm -rf -- %s failed, code %d' % (tmpdir, ec))
784 print_exception(sys.exc_info(),
785 '\nadt-run: error cleaning up:\n')
788 #---------- registration, installation etc. of .deb's: Binaries
790 def determine_package(act):
791 cmd = 'dpkg-deb --info --'.split(' ')+[act.path.read(),'control']
792 running = Popen(cmd, stdout=PIPE)
793 output = running.communicate()[0]
795 if rc: badpkg('failed to parse binary package, code %d' % rc)
796 re = regexp.compile('^\s*Package\s*:\s*([0-9a-z][-+.0-9a-z]*)\s*$')
798 for l in '\n'.split(output):
801 if act.pkg: badpkg('two Package: lines in control file')
803 if not act.pkg: badpkg('no good Package: line in control file')
807 b.dir = tmpdir+'/binaries'
809 if opts.gnupghome is None:
810 opts.gnupghome = tmpdir+'/gnupg'
813 for x in ['pubring','secring']:
814 os.stat(opts.gnupghome + '/' + x + '.gpg')
816 if oe.errno != errno.ENOENT: raise
818 try: os.mkdir(opts.gnupghome, 0700)
819 except IOError, oe: if oe.errno != errno.EEXIST: raise
822 exec >key-gen-log 2>&1
823 cat <<"END" >key-gen-params
827 Name-Real: autopkgtest per-run key
828 Name-Comment: do not trust this key
829 Name-Email: autopkgtest@example.com
832 gpg --homedir="$1" --batch --gen-key key-gen-params
834 cmdl = ['sh','-ec',script,'x',opts.gnupghome]
835 rc = subprocess.call(cmdl)
838 f = open(opts.gnupghome+'/key-gen-log')
840 except IOError, e: tp = e
841 print >>sys.stderr, tp
842 bomb('key generation failed, code %d' % rc)
847 b.tbpath = testbed.scratch.append('/binaries')
851 def register(b, act, pkg, path, forwhat, blamed):
852 if act.ah['deb_'+forwhat] == 'ignore': return
854 b.blamed += testbed.blamed
857 leafname = pkg+'.deb'
858 dest = b.dir+'/'+leafname
862 if oe.errno != errno.ENOENT: raise e
864 try: os.link(here, dest)
866 if oe.errno != errno.EXDEV: raise e
867 shutil.copy(here, dest)
869 if act.ah['deb_'+forwhat] == 'install':
870 b.install.append(pkg)
875 apt-ftparchive packages . >Packages
877 apt-ftparchive release . >Release
878 gpg --homedir="$2" --batch --detach-sign --armour -o Release.gpg Release
879 gpg --homedir="$2" --batch --export >archive-key.pgp
881 cmdl = ['sh','-ec',script,'x',b.dir,opts.gnupghome]
882 rc = subprocess.call(cmd)
883 if rc: bomb('apt-ftparchive or signature failed, code %d' % rc)
885 tbp = b.tbpath.write(True)
886 testbed.command('copydown', (b.dir+'/', tbp+'/'))
888 se = TemporaryPath('%s-aptkey-stderr' % act.what)
890 apt-key add archive-key.pgp
891 echo "deb file:///'+tbp+'/ /" >/etc/apt/sources.list.d/autopkgtest
893 rc = testbed.commandr1(['execute',
894 ','.join(map(urllib.quote, ['sh','-ec','script']))],
895 '/dev/null', '/dev/null', se.write(True), tbp)
896 if rc: bomb('apt setup failed with exit code %d' % rc, se)
898 testbed.blamed += b.blamed
900 for pkg in b.install:
902 se = TemporaryPath('%s-install-%s-stderr' % (act.what,pkg))
903 rc = testbed.commandr1('execute','apt-get,-qy,install,'+pkg,
904 '/dev/null','/dev/null',se.ontb(),
905 tb.scratch.read(True))
907 badpkg("installation of %s failed, exit code %d"
910 #---------- processing of sources (building)
912 def source_rules_command(act,script,which,work,results_lines=0):
913 script = "exec 3>&1 >&2\n" + '\n'.join(script)
914 so = TemporaryPath('%s-%s-results' % (what,which))
915 se = TemporaryPath('%s-%s-log' & (what,which))
916 rc = testbed.commandr1(['execute',
917 ','.join(map(urllib.quote, ['sh','-xec',script]))],
918 '/dev/null', so.write(True), se.write(True), work.write(True))
919 results = file(so.read()).read().split("\n")
921 badpkg_se("%s failed with exit code %d" % (which,rc), se)
922 if results_lines is not None and len(results) != results_lines:
923 badpkg_se("got %d lines of results from %s where %d expected"
924 % (len(results), which, results_lines), se)
925 if results_lines==1: return results[0]
928 def build_source(act):
929 act.blame = 'arg:'+act.path.spec()
930 testbed.blame(act.blame)
931 testbed.needs_reset()
933 what = act.ah['what']
935 basename = dsc.spec; if basename is None: basename = 'source.dsc'
936 dsc_what = what+'/'+basename
938 dsc_file = open(dsc.read())
940 fre = regexp.compile('^\s+[0-9a-f]+\s+\d+\s+([^/.][^/]*)$')
942 if l.startswith('Files:'): in_files = True
943 elif l.startswith('#'): pass
944 elif not l.startswith(' '):
946 if l.startswith('Source:'):
947 act.blame = 'dsc:'+l[7:].strip()
948 testbed.blame(act.blame)
949 elif not in_files: pass
950 if not dsc.spec_tb: continue
952 if not m: badpkg(".dsc contains unparseable line"
953 " in Files: `%s'" % (`dsc`,l))
954 subfile = dsc.enclosingdir().append('/'+m.groups(0))
958 work = AccumulationPath(what+'/build', dir=True)
961 'cd '+work.write(True),
962 'gdebi '+dsc.read(True),
963 'dpkg-source -x '+dsc.read(True),
966 opts.user_wrap('debian/rules build'),
968 result_pwd = source_rules_command(act,script,what,'build',work,1)
970 if os.path.dirname(result_pwd) != work.read(True):
971 badpkg_se("results dir `%s' is not in expected parent dir `%s'"
972 % (results[0], work.read(True)), se)
974 act.tests_tree = work.append('/'+os.path.basename(results[0]))
975 if act.ah['dsc_tests']:
976 act.tests_tree.preserve_now()
978 act.blamed = testbed.blamed.copy()
981 if act.ah['dsc_filter'] != '_':
983 'cd '+work.write(True)+'/*/.',
984 opts.user_wrap(opts.fakeroot+' debian/rules binary'),
988 result_debs = source_rules_command(act,script,what,
989 'debian/rules binary',work,1)
990 if result_debs == '*': debs = []
991 else: debs = debs.split(' ')
992 re = regexp.compile('^([-+.0-9a-z]+)_[^_/]+(?:_[^_/]+)\.deb$')
995 if not m: badpkg("badly-named binary `%s'" % deb)
997 for pat in act.ah['dsc_filter'].split(','):
998 if fnmatch.fnmatchcase(pkg,pat):
999 deb_path = work.read()+'/'+deb
1000 deb_what = pkg+'_'+what+'.deb'
1001 bin = InputPath(deb_what,deb_path,True)
1003 binaries.register(act,pkg,bin,'builds',
1005 act.binaries.append((pkg,bin))
1008 #---------- main processing loop and main program
1010 def process_actions():
1012 binaries = Binaries()
1015 for act in opts.actions:
1017 if act.kind == 'deb':
1018 blame('arg:'+path.spec)
1019 determine_package(act)
1020 blame('deb:'+act.pkg)
1021 binaries.register(act,act.pkg,act.path,'builds',
1023 if act.kind == 'dsc':
1027 control_override = None
1028 for act in opts.actions:
1030 if act.kind == 'control':
1031 control_override = act.path
1032 if act.kind == 'deb':
1033 binaries.register(act,act.pkg,act.path,'tests',
1035 if act.kind == 'dsc':
1036 for (pkg,bin) in act.binaries:
1037 binaries.register(act,pkg,bin,'tests',
1039 if not act.ah['dsc_tests']: continue
1040 stanzas = read_control(act, act.tests_tree,
1042 testbed.blamed += act.blamed
1043 run_tests(act, stanzas)
1044 control_override = None
1045 if act.kind == 'tree':
1046 testbed.blame('arg:'+act.path.spec)
1047 stanzas = read_control(act, act.path,
1049 run_tests(act, stanzas)
1050 control_override = None
1057 except SystemExit, se:
1060 tmpdir = tempfile.mkdtemp()
1066 ec = print_exception(sys.exc_info(), '')