2010-11-12 05:24:46 -08:00
|
|
|
#!/usr/bin/python
|
2010-11-13 02:47:54 -08:00
|
|
|
import sys, os, subprocess, glob, time
|
2010-11-13 04:36:44 -08:00
|
|
|
import options, jwack
|
2010-11-12 05:24:46 -08:00
|
|
|
|
|
|
|
|
optspec = """
|
|
|
|
|
redo [targets...]
|
|
|
|
|
--
|
2010-11-13 04:36:44 -08:00
|
|
|
j,jobs= maximum number of jobs to build at once
|
2010-11-12 07:03:06 -08:00
|
|
|
d,debug print dependency checks as they happen
|
2010-11-12 07:10:55 -08:00
|
|
|
v,verbose print commands as they are run
|
2010-11-12 05:24:46 -08:00
|
|
|
"""
|
|
|
|
|
o = options.Options('redo', optspec)
|
|
|
|
|
(opt, flags, extra) = o.parse(sys.argv[1:])
|
|
|
|
|
|
2010-11-13 01:40:37 -08:00
|
|
|
targets = extra or ['all']
|
2010-11-12 05:24:46 -08:00
|
|
|
|
2010-11-13 00:53:55 -08:00
|
|
|
if opt.debug:
|
|
|
|
|
os.environ['REDO_DEBUG'] = '1'
|
|
|
|
|
if opt.verbose:
|
|
|
|
|
os.environ['REDO_VERBOSE'] = '1'
|
|
|
|
|
|
|
|
|
|
if not os.environ.get('REDO_BASE', ''):
|
|
|
|
|
base = os.path.commonprefix([os.path.abspath(os.path.dirname(t))
|
|
|
|
|
for t in targets] + [os.getcwd()])
|
|
|
|
|
bsplit = base.split('/')
|
|
|
|
|
for i in range(len(bsplit)-1, 0, -1):
|
|
|
|
|
newbase = '%s/.redo' % '/'.join(bsplit[:i])
|
|
|
|
|
if os.path.exists(newbase):
|
|
|
|
|
base = newbase
|
|
|
|
|
break
|
|
|
|
|
os.environ['REDO_BASE'] = base
|
2010-11-13 01:55:07 -08:00
|
|
|
os.environ['REDO_STARTDIR'] = os.getcwd()
|
2010-11-13 00:53:55 -08:00
|
|
|
|
2010-11-13 02:47:54 -08:00
|
|
|
# FIXME: just wiping out all the locks is kind of cheating. But we
|
|
|
|
|
# only do this from the toplevel redo process, so unless the user
|
|
|
|
|
# deliberately starts more than one redo on the same repository, it's
|
|
|
|
|
# sort of ok.
|
2010-11-13 04:36:44 -08:00
|
|
|
for f in glob.glob('%s/.redo/lock^*' % base):
|
|
|
|
|
os.unlink(f)
|
2010-11-13 02:47:54 -08:00
|
|
|
|
2010-11-13 00:53:55 -08:00
|
|
|
import vars
|
|
|
|
|
from helpers import *
|
|
|
|
|
|
2010-11-12 05:24:46 -08:00
|
|
|
|
2010-11-13 01:21:59 -08:00
|
|
|
class BuildError(Exception):
|
|
|
|
|
pass
|
2010-11-13 02:47:54 -08:00
|
|
|
class BuildLocked(Exception):
|
|
|
|
|
pass
|
2010-11-13 01:21:59 -08:00
|
|
|
|
|
|
|
|
|
2010-11-12 05:24:46 -08:00
|
|
|
def find_do_file(t):
|
2010-11-12 07:03:06 -08:00
|
|
|
dofile = '%s.do' % t
|
|
|
|
|
if os.path.exists(dofile):
|
|
|
|
|
add_dep(t, 'm', dofile)
|
|
|
|
|
return dofile
|
2010-11-12 05:24:46 -08:00
|
|
|
else:
|
2010-11-12 07:03:06 -08:00
|
|
|
add_dep(t, 'c', dofile)
|
2010-11-12 05:24:46 -08:00
|
|
|
return None
|
2010-11-12 07:03:06 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def stamp(t):
|
2010-11-12 22:47:03 -08:00
|
|
|
stampfile = sname('stamp', t)
|
2010-11-13 00:45:49 -08:00
|
|
|
depfile = sname('dep', t)
|
2010-11-13 00:11:34 -08:00
|
|
|
if not os.path.exists(vars.BASE + '/.redo'):
|
2010-11-12 22:50:23 -08:00
|
|
|
# .redo might not exist in a 'make clean' target
|
|
|
|
|
return
|
2010-11-12 07:03:06 -08:00
|
|
|
open(stampfile, 'w').close()
|
2010-11-13 00:45:49 -08:00
|
|
|
open(depfile, 'a').close()
|
2010-11-12 07:03:06 -08:00
|
|
|
try:
|
|
|
|
|
mtime = os.stat(t).st_mtime
|
|
|
|
|
except OSError:
|
|
|
|
|
mtime = 0
|
|
|
|
|
os.utime(stampfile, (mtime, mtime))
|
|
|
|
|
|
2010-11-12 05:24:46 -08:00
|
|
|
|
2010-11-12 22:14:39 -08:00
|
|
|
def _preexec(t):
|
2010-11-12 23:14:02 -08:00
|
|
|
os.environ['REDO_TARGET'] = t
|
2010-11-13 00:11:34 -08:00
|
|
|
os.environ['REDO_DEPTH'] = vars.DEPTH + ' '
|
2010-11-12 23:14:58 -08:00
|
|
|
dn = os.path.dirname(t)
|
|
|
|
|
if dn:
|
|
|
|
|
os.chdir(dn)
|
2010-11-12 22:14:39 -08:00
|
|
|
|
|
|
|
|
|
2010-11-13 02:47:54 -08:00
|
|
|
def _build(t):
|
2010-11-12 22:47:03 -08:00
|
|
|
unlink(sname('dep', t))
|
|
|
|
|
open(sname('dep', t), 'w').close()
|
2010-11-12 05:24:46 -08:00
|
|
|
dofile = find_do_file(t)
|
|
|
|
|
if not dofile:
|
|
|
|
|
if os.path.exists(t): # an existing source file
|
2010-11-12 07:03:06 -08:00
|
|
|
stamp(t)
|
2010-11-12 05:24:46 -08:00
|
|
|
return # success
|
|
|
|
|
else:
|
2010-11-13 01:21:59 -08:00
|
|
|
raise BuildError('no rule to make %r' % t)
|
2010-11-13 00:45:49 -08:00
|
|
|
stamp(dofile)
|
2010-11-12 05:24:46 -08:00
|
|
|
unlink(t)
|
|
|
|
|
tmpname = '%s.redo.tmp' % t
|
|
|
|
|
unlink(tmpname)
|
|
|
|
|
f = open(tmpname, 'w+')
|
2010-11-13 01:29:27 -08:00
|
|
|
argv = [os.environ.get('SHELL', 'sh'), '-e',
|
|
|
|
|
os.path.basename(dofile),
|
2010-11-12 23:14:58 -08:00
|
|
|
os.path.basename(t), 'FIXME', os.path.basename(tmpname)]
|
2010-11-13 00:11:34 -08:00
|
|
|
if vars.VERBOSE:
|
2010-11-12 07:10:55 -08:00
|
|
|
argv[1] += 'v'
|
2010-11-13 01:55:07 -08:00
|
|
|
log('%s\n' % relpath(t, vars.STARTDIR))
|
2010-11-12 22:14:39 -08:00
|
|
|
rv = subprocess.call(argv, preexec_fn=lambda: _preexec(t),
|
|
|
|
|
stdout=f.fileno())
|
2010-11-12 05:24:46 -08:00
|
|
|
st = os.stat(tmpname)
|
2010-11-12 22:47:03 -08:00
|
|
|
stampfile = sname('stamp', t)
|
2010-11-12 07:03:06 -08:00
|
|
|
if rv==0:
|
|
|
|
|
if st.st_size:
|
|
|
|
|
os.rename(tmpname, t)
|
|
|
|
|
else:
|
|
|
|
|
unlink(tmpname)
|
|
|
|
|
stamp(t)
|
2010-11-12 05:24:46 -08:00
|
|
|
else:
|
|
|
|
|
unlink(tmpname)
|
2010-11-12 07:03:06 -08:00
|
|
|
unlink(stampfile)
|
2010-11-12 05:24:46 -08:00
|
|
|
f.close()
|
|
|
|
|
if rv != 0:
|
2010-11-13 01:21:59 -08:00
|
|
|
raise BuildError('%s: exit code %d' % (t,rv))
|
2010-11-12 05:24:46 -08:00
|
|
|
|
2010-11-12 07:03:06 -08:00
|
|
|
|
2010-11-13 02:47:54 -08:00
|
|
|
def build(t):
|
2010-11-13 04:36:44 -08:00
|
|
|
mkdirp('%s/.redo' % vars.BASE)
|
2010-11-13 02:47:54 -08:00
|
|
|
lockname = sname('lock', t)
|
|
|
|
|
try:
|
|
|
|
|
fd = os.open(lockname, os.O_CREAT|os.O_EXCL)
|
|
|
|
|
except OSError, e:
|
|
|
|
|
if e.errno == errno.EEXIST:
|
|
|
|
|
log('%s (locked...)\n' % relpath(t, vars.STARTDIR))
|
2010-11-13 04:36:44 -08:00
|
|
|
os._exit(199)
|
2010-11-13 02:47:54 -08:00
|
|
|
else:
|
|
|
|
|
raise
|
|
|
|
|
os.close(fd)
|
|
|
|
|
try:
|
2010-11-13 04:36:44 -08:00
|
|
|
try:
|
|
|
|
|
return _build(t)
|
|
|
|
|
except BuildError, e:
|
|
|
|
|
err('%s\n' % e)
|
|
|
|
|
os._exit(1)
|
2010-11-13 02:47:54 -08:00
|
|
|
finally:
|
|
|
|
|
unlink(lockname)
|
|
|
|
|
|
|
|
|
|
|
2010-11-13 04:36:44 -08:00
|
|
|
def main():
|
2010-11-13 02:09:42 -08:00
|
|
|
retcode = 0
|
2010-11-13 02:47:54 -08:00
|
|
|
locked = {}
|
2010-11-13 04:36:44 -08:00
|
|
|
waits = {}
|
2010-11-13 02:09:42 -08:00
|
|
|
for t in targets:
|
|
|
|
|
if os.path.exists('%s/all.do' % t):
|
|
|
|
|
# t is a directory, but it has a default target
|
|
|
|
|
t = '%s/all' % t
|
2010-11-13 04:36:44 -08:00
|
|
|
waits[t] = jwack.start_job(t, lambda: build(t))
|
|
|
|
|
jwack.wait_all()
|
|
|
|
|
for t,pd in waits.items():
|
|
|
|
|
assert(pd.rv != None)
|
|
|
|
|
if pd.rv == 199:
|
|
|
|
|
# target was locked
|
2010-11-13 02:47:54 -08:00
|
|
|
locked[t] = 1
|
2010-11-13 04:36:44 -08:00
|
|
|
elif pd.rv:
|
|
|
|
|
err('%s: exit code was %r\n' % (t, pd.rv))
|
|
|
|
|
retcode = 1
|
2010-11-13 02:47:54 -08:00
|
|
|
while locked:
|
2010-11-13 04:36:44 -08:00
|
|
|
for t in locked.keys():
|
2010-11-13 02:47:54 -08:00
|
|
|
lockname = sname('lock', t)
|
|
|
|
|
stampname = sname('stamp', t)
|
|
|
|
|
if not os.path.exists(lockname):
|
|
|
|
|
relp = relpath(t, vars.STARTDIR)
|
|
|
|
|
log('%s (...unlocked!)\n' % relp)
|
|
|
|
|
if not os.path.exists(stampname):
|
|
|
|
|
err('%s: failed in another thread\n' % relp)
|
|
|
|
|
retcode = 2
|
2010-11-13 04:36:44 -08:00
|
|
|
del locked[t]
|
2010-11-13 02:47:54 -08:00
|
|
|
time.sleep(0.2)
|
2010-11-13 04:36:44 -08:00
|
|
|
return retcode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not vars.DEPTH:
|
|
|
|
|
# toplevel call to redo
|
|
|
|
|
exenames = [os.path.abspath(sys.argv[0]), os.path.realpath(sys.argv[0])]
|
|
|
|
|
if exenames[0] == exenames[1]:
|
|
|
|
|
exenames = [exenames[0]]
|
|
|
|
|
dirnames = [os.path.dirname(p) for p in exenames]
|
|
|
|
|
os.environ['PATH'] = ':'.join(dirnames) + ':' + os.environ['PATH']
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
j = atoi(opt.jobs or 1)
|
|
|
|
|
if j < 1 or j > 1000:
|
|
|
|
|
err('invalid --jobs value: %r\n' % opt.jobs)
|
|
|
|
|
jwack.setup(j)
|
|
|
|
|
try:
|
|
|
|
|
retcode = main()
|
|
|
|
|
finally:
|
|
|
|
|
jwack.force_return_tokens()
|
|
|
|
|
if retcode:
|
|
|
|
|
err('exiting: %d\n' % retcode)
|
2010-11-13 02:09:42 -08:00
|
|
|
sys.exit(retcode)
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
sys.exit(200)
|