2010-11-12 05:24:46 -08:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
import sys, os, subprocess
|
|
|
|
|
import options
|
|
|
|
|
from helpers import *
|
|
|
|
|
|
|
|
|
|
optspec = """
|
|
|
|
|
redo [targets...]
|
|
|
|
|
--
|
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 07:03:06 -08:00
|
|
|
ifchange only redo if the file is modified or deleted
|
|
|
|
|
ifcreate only redo if the file is created
|
2010-11-12 05:24:46 -08:00
|
|
|
"""
|
|
|
|
|
o = options.Options('redo', optspec)
|
|
|
|
|
(opt, flags, extra) = o.parse(sys.argv[1:])
|
|
|
|
|
|
|
|
|
|
targets = extra or ['it']
|
|
|
|
|
|
|
|
|
|
|
2010-11-12 23:14:58 -08:00
|
|
|
def relpath(t, base):
|
2010-11-12 22:47:03 -08:00
|
|
|
t = os.path.abspath(t)
|
|
|
|
|
tparts = t.split('/')
|
2010-11-12 23:14:58 -08:00
|
|
|
bparts = base.split('/')
|
2010-11-12 22:47:03 -08:00
|
|
|
for tp,bp in zip(tparts,bparts):
|
|
|
|
|
if tp != bp:
|
|
|
|
|
break
|
|
|
|
|
tparts.pop(0)
|
|
|
|
|
bparts.pop(0)
|
|
|
|
|
while bparts:
|
|
|
|
|
tparts.insert(0, '..')
|
|
|
|
|
bparts.pop(0)
|
2010-11-12 23:14:58 -08:00
|
|
|
return '/'.join(tparts)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sname(typ, t):
|
|
|
|
|
# FIXME: t.replace(...) is non-reversible and non-unique here!
|
|
|
|
|
tnew = relpath(t, REDO_BASE)
|
|
|
|
|
#log('sname: (%r) %r -> %r\n' % (REDO_BASE, t, tnew))
|
2010-11-12 22:47:03 -08:00
|
|
|
return REDO_BASE + ('/.redo/%s^%s' % (typ, tnew.replace('/', '^')))
|
|
|
|
|
|
|
|
|
|
|
2010-11-12 23:54:57 -08:00
|
|
|
def _log(s):
|
2010-11-12 07:03:06 -08:00
|
|
|
sys.stdout.flush()
|
2010-11-12 23:54:57 -08:00
|
|
|
sys.stderr.write(s)
|
2010-11-12 07:03:06 -08:00
|
|
|
sys.stderr.flush()
|
|
|
|
|
|
|
|
|
|
|
2010-11-12 23:54:57 -08:00
|
|
|
def _clog(s):
|
|
|
|
|
_log('\x1b[32mredo: %s\x1b[1m%s\x1b[m' % (REDO_DEPTH, s))
|
|
|
|
|
def _bwlog(s):
|
|
|
|
|
_log('redo: %s%s' % (REDO_DEPTH, s))
|
|
|
|
|
if os.isatty(2):
|
|
|
|
|
log = _clog
|
|
|
|
|
else:
|
|
|
|
|
log = _bwlog
|
|
|
|
|
|
2010-11-12 07:03:06 -08:00
|
|
|
def debug(s):
|
|
|
|
|
if REDO_DEBUG:
|
2010-11-12 23:54:57 -08:00
|
|
|
_log('redo: %s%s' % (REDO_DEPTH, s))
|
2010-11-12 07:03:06 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_dep(t, mode, dep):
|
2010-11-12 22:47:03 -08:00
|
|
|
open(sname('dep', t), 'a').write('%s %s\n' % (mode, dep))
|
2010-11-12 07:03:06 -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)
|
|
|
|
|
if dirty_deps(dofile, depth = ''):
|
|
|
|
|
build(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 _dirty_deps(t, depth):
|
|
|
|
|
debug('%s?%s\n' % (depth, t))
|
2010-11-12 22:47:03 -08:00
|
|
|
if not os.path.exists(sname('stamp', t)):
|
2010-11-12 07:03:06 -08:00
|
|
|
debug('%s-- DIRTY (no stamp)\n' % depth)
|
|
|
|
|
return True
|
|
|
|
|
|
2010-11-12 22:47:03 -08:00
|
|
|
stamptime = os.stat(sname('stamp', t)).st_mtime
|
2010-11-12 07:03:06 -08:00
|
|
|
try:
|
|
|
|
|
realtime = os.stat(t).st_mtime
|
|
|
|
|
except OSError:
|
|
|
|
|
realtime = 0
|
|
|
|
|
|
|
|
|
|
if stamptime != realtime:
|
|
|
|
|
debug('%s-- DIRTY (mtime)\n' % depth)
|
|
|
|
|
return True
|
2010-11-12 05:24:46 -08:00
|
|
|
|
2010-11-12 22:47:03 -08:00
|
|
|
for sub in open(sname('dep', t)).readlines():
|
2010-11-12 07:03:06 -08:00
|
|
|
assert(sub[0] in ('c','m'))
|
|
|
|
|
assert(sub[1] == ' ')
|
|
|
|
|
assert(sub[-1] == '\n')
|
|
|
|
|
mode = sub[0]
|
|
|
|
|
name = sub[2:-1]
|
|
|
|
|
if mode == 'c':
|
|
|
|
|
if os.path.exists(name):
|
|
|
|
|
debug('%s-- DIRTY (created)\n' % depth)
|
|
|
|
|
return True
|
|
|
|
|
elif mode == 'm':
|
|
|
|
|
if dirty_deps(name, depth + ' '):
|
|
|
|
|
#debug('%s-- DIRTY (sub)\n' % depth)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dirty_deps(t, depth):
|
|
|
|
|
if _dirty_deps(t, depth):
|
2010-11-12 22:47:03 -08:00
|
|
|
unlink(sname('stamp', t)) # short circuit future checks
|
2010-11-12 07:03:06 -08:00
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def stamp(t):
|
2010-11-12 22:47:03 -08:00
|
|
|
stampfile = sname('stamp', t)
|
2010-11-12 22:50:23 -08:00
|
|
|
if not os.path.exists(REDO_BASE + '/.redo'):
|
|
|
|
|
# .redo might not exist in a 'make clean' target
|
|
|
|
|
return
|
2010-11-12 07:03:06 -08:00
|
|
|
open(stampfile, 'w').close()
|
|
|
|
|
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
|
|
|
|
|
os.environ['REDO_DEPTH'] = REDO_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-12 05:24:46 -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:
|
|
|
|
|
raise Exception('no rule to make %r' % t)
|
|
|
|
|
unlink(t)
|
|
|
|
|
tmpname = '%s.redo.tmp' % t
|
|
|
|
|
unlink(tmpname)
|
|
|
|
|
f = open(tmpname, 'w+')
|
2010-11-12 23:14:58 -08:00
|
|
|
argv = ['sh', '-e', os.path.basename(dofile),
|
|
|
|
|
os.path.basename(t), 'FIXME', os.path.basename(tmpname)]
|
2010-11-12 07:10:55 -08:00
|
|
|
if REDO_VERBOSE:
|
|
|
|
|
argv[1] += 'v'
|
2010-11-12 07:03:06 -08:00
|
|
|
log('%s\n' % t)
|
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 05:31:07 -08:00
|
|
|
#log('rv: %d (%d bytes) (%r)\n' % (rv, st.st_size, dofile))
|
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:
|
|
|
|
|
raise Exception('non-zero return code building %r' % t)
|
|
|
|
|
|
2010-11-12 07:03:06 -08:00
|
|
|
|
2010-11-12 22:14:39 -08:00
|
|
|
if opt.debug:
|
2010-11-12 23:14:02 -08:00
|
|
|
os.environ['REDO_DEBUG'] = '1'
|
2010-11-12 22:14:39 -08:00
|
|
|
if opt.verbose:
|
2010-11-12 23:14:02 -08:00
|
|
|
os.environ['REDO_VERBOSE'] = '1'
|
2010-11-12 17:16:03 -08:00
|
|
|
assert(not (opt.ifchange and opt.ifcreate))
|
|
|
|
|
|
2010-11-12 23:14:02 -08:00
|
|
|
if not os.environ.get('REDO_BASE', ''):
|
2010-11-12 22:47:03 -08:00
|
|
|
base = os.path.commonprefix([os.path.abspath(os.path.dirname(t))
|
2010-11-12 23:20:37 -08:00
|
|
|
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
|
2010-11-12 23:14:02 -08:00
|
|
|
os.environ['REDO_BASE'] = base
|
2010-11-12 22:14:39 -08:00
|
|
|
|
2010-11-12 22:47:03 -08:00
|
|
|
from vars import *
|
2010-11-12 07:03:06 -08:00
|
|
|
|
2010-11-12 17:16:03 -08:00
|
|
|
if not REDO_DEPTH:
|
2010-11-12 22:14:39 -08:00
|
|
|
# toplevel call to redo
|
2010-11-12 17:16:03 -08:00
|
|
|
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]
|
2010-11-12 23:14:02 -08:00
|
|
|
os.environ['PATH'] = ':'.join(dirnames) + ':' + os.environ['PATH']
|
2010-11-12 17:16:03 -08:00
|
|
|
|
2010-11-12 23:14:58 -08:00
|
|
|
startdir = os.getcwd()
|
2010-11-12 05:24:46 -08:00
|
|
|
for t in targets:
|
2010-11-12 23:58:39 -08:00
|
|
|
mkdirp('%s/.redo' % REDO_BASE)
|
2010-11-12 23:14:58 -08:00
|
|
|
os.chdir(startdir)
|
|
|
|
|
|
2010-11-12 07:03:06 -08:00
|
|
|
if REDO_TARGET:
|
|
|
|
|
add_dep(REDO_TARGET, opt.ifcreate and 'c' or 'm', t)
|
|
|
|
|
if opt.ifcreate:
|
|
|
|
|
pass # just adding the dependency (above) is enough
|
|
|
|
|
elif opt.ifchange:
|
|
|
|
|
if dirty_deps(t, depth = ''):
|
|
|
|
|
build(t)
|
|
|
|
|
else:
|
|
|
|
|
build(t)
|