Add actual dependency checking.
This commit is contained in:
parent
ee8a3c8c6e
commit
9a45f066f8
2 changed files with 130 additions and 20 deletions
24
helpers.py
24
helpers.py
|
|
@ -1,12 +1,6 @@
|
||||||
import sys, os, errno
|
import sys, os, errno
|
||||||
|
|
||||||
|
|
||||||
def log(s):
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.write(s)
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def unlink(f):
|
def unlink(f):
|
||||||
"""Delete a file at path 'f' if it currently exists.
|
"""Delete a file at path 'f' if it currently exists.
|
||||||
|
|
||||||
|
|
@ -20,3 +14,21 @@ def unlink(f):
|
||||||
pass # it doesn't exist, that's what you asked for
|
pass # it doesn't exist, that's what you asked for
|
||||||
|
|
||||||
|
|
||||||
|
def mkdirp(d, mode=None):
|
||||||
|
"""Recursively create directories on path 'd'.
|
||||||
|
|
||||||
|
Unlike os.makedirs(), it doesn't raise an exception if the last element of
|
||||||
|
the path already exists.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if mode:
|
||||||
|
os.makedirs(d, mode)
|
||||||
|
else:
|
||||||
|
os.makedirs(d)
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno == errno.EEXIST:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
126
redo.py
126
redo.py
|
|
@ -6,7 +6,9 @@ from helpers import *
|
||||||
optspec = """
|
optspec = """
|
||||||
redo [targets...]
|
redo [targets...]
|
||||||
--
|
--
|
||||||
ifchange something something
|
d,debug print dependency checks as they happen
|
||||||
|
ifchange only redo if the file is modified or deleted
|
||||||
|
ifcreate only redo if the file is created
|
||||||
"""
|
"""
|
||||||
o = options.Options('redo', optspec)
|
o = options.Options('redo', optspec)
|
||||||
(opt, flags, extra) = o.parse(sys.argv[1:])
|
(opt, flags, extra) = o.parse(sys.argv[1:])
|
||||||
|
|
@ -14,41 +16,137 @@ o = options.Options('redo', optspec)
|
||||||
targets = extra or ['it']
|
targets = extra or ['it']
|
||||||
|
|
||||||
|
|
||||||
def find_do_file(t):
|
def log(s):
|
||||||
p = '%s.do' % t
|
sys.stdout.flush()
|
||||||
if os.path.exists(p):
|
sys.stderr.write('redo: %s%s' % (REDO_DEPTH, s))
|
||||||
return p
|
sys.stderr.flush()
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
def debug(s):
|
||||||
|
if REDO_DEBUG:
|
||||||
|
log(s)
|
||||||
|
|
||||||
|
|
||||||
|
def add_dep(t, mode, dep):
|
||||||
|
open('.redo/dep.%s' % t, 'a').write('%s %s\n' % (mode, dep))
|
||||||
|
|
||||||
|
|
||||||
|
def find_do_file(t):
|
||||||
|
dofile = '%s.do' % t
|
||||||
|
if os.path.exists(dofile):
|
||||||
|
add_dep(t, 'm', dofile)
|
||||||
|
if dirty_deps(dofile, depth = ''):
|
||||||
|
build(dofile)
|
||||||
|
return dofile
|
||||||
|
else:
|
||||||
|
add_dep(t, 'c', dofile)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _dirty_deps(t, depth):
|
||||||
|
debug('%s?%s\n' % (depth, t))
|
||||||
|
if not os.path.exists('.redo/stamp.%s' % t):
|
||||||
|
debug('%s-- DIRTY (no stamp)\n' % depth)
|
||||||
|
return True
|
||||||
|
|
||||||
|
stamptime = os.stat('.redo/stamp.%s' % t).st_mtime
|
||||||
|
try:
|
||||||
|
realtime = os.stat(t).st_mtime
|
||||||
|
except OSError:
|
||||||
|
realtime = 0
|
||||||
|
|
||||||
|
if stamptime != realtime:
|
||||||
|
debug('%s-- DIRTY (mtime)\n' % depth)
|
||||||
|
return True
|
||||||
|
|
||||||
|
for sub in open('.redo/dep.%s' % t).readlines():
|
||||||
|
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):
|
||||||
|
unlink('.redo/stamp.%s' % t) # short circuit future checks
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def stamp(t):
|
||||||
|
stampfile = '.redo/stamp.%s' % t
|
||||||
|
open(stampfile, 'w').close()
|
||||||
|
try:
|
||||||
|
mtime = os.stat(t).st_mtime
|
||||||
|
except OSError:
|
||||||
|
mtime = 0
|
||||||
|
os.utime(stampfile, (mtime, mtime))
|
||||||
|
|
||||||
|
|
||||||
def build(t):
|
def build(t):
|
||||||
|
unlink('.redo/dep.%s' % t)
|
||||||
|
open('.redo/dep.%s' % t, 'w').close()
|
||||||
dofile = find_do_file(t)
|
dofile = find_do_file(t)
|
||||||
if not dofile:
|
if not dofile:
|
||||||
if os.path.exists(t): # an existing source file
|
if os.path.exists(t): # an existing source file
|
||||||
|
stamp(t)
|
||||||
return # success
|
return # success
|
||||||
else:
|
else:
|
||||||
raise Exception('no rule to make %r' % t)
|
raise Exception('no rule to make %r' % t)
|
||||||
unlink(t)
|
unlink(t)
|
||||||
os.putenv('REDO_TARGET', t)
|
os.putenv('REDO_TARGET', t)
|
||||||
depth = os.getenv('REDO_DEPTH', '')
|
os.putenv('REDO_DEPTH', REDO_DEPTH + ' ')
|
||||||
os.putenv('REDO_DEPTH', depth + ' ')
|
|
||||||
tmpname = '%s.redo.tmp' % t
|
tmpname = '%s.redo.tmp' % t
|
||||||
unlink(tmpname)
|
unlink(tmpname)
|
||||||
f = open(tmpname, 'w+')
|
f = open(tmpname, 'w+')
|
||||||
argv = ['sh', '-e', dofile, t, 'FIXME', tmpname]
|
argv = ['sh', '-e', dofile, t, 'FIXME', tmpname]
|
||||||
log('redo: %s%s\n' % (depth, t))
|
log('%s\n' % t)
|
||||||
rv = subprocess.call(argv, stdout=f.fileno())
|
rv = subprocess.call(argv, stdout=f.fileno())
|
||||||
st = os.stat(tmpname)
|
st = os.stat(tmpname)
|
||||||
#log('rv: %d (%d bytes) (%r)\n' % (rv, st.st_size, dofile))
|
#log('rv: %d (%d bytes) (%r)\n' % (rv, st.st_size, dofile))
|
||||||
if rv==0 and st.st_size:
|
stampfile = '.redo/stamp.%s' % t
|
||||||
os.rename(tmpname, t)
|
if rv==0:
|
||||||
#log('made %r\n' % t)
|
if st.st_size:
|
||||||
|
os.rename(tmpname, t)
|
||||||
|
else:
|
||||||
|
unlink(tmpname)
|
||||||
|
stamp(t)
|
||||||
else:
|
else:
|
||||||
unlink(tmpname)
|
unlink(tmpname)
|
||||||
|
unlink(stampfile)
|
||||||
f.close()
|
f.close()
|
||||||
if rv != 0:
|
if rv != 0:
|
||||||
raise Exception('non-zero return code building %r' % t)
|
raise Exception('non-zero return code building %r' % t)
|
||||||
|
|
||||||
|
|
||||||
|
mkdirp('.redo')
|
||||||
|
REDO_TARGET = os.getenv('REDO_TARGET', '')
|
||||||
|
REDO_DEPTH = os.getenv('REDO_DEPTH', '')
|
||||||
|
|
||||||
|
assert(not (opt.ifchange and opt.ifcreate))
|
||||||
|
if opt.debug:
|
||||||
|
REDO_DEBUG = 1
|
||||||
|
os.putenv('REDO_DEBUG', '1')
|
||||||
|
else:
|
||||||
|
REDO_DEBUG = os.getenv('REDO_DEBUG', '') and 1 or 0
|
||||||
|
|
||||||
for t in targets:
|
for t in targets:
|
||||||
build(t)
|
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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue