Move 'redo --ifchange' into 'redo-ifchange' to match djb's style.

It does simplify the logic of both redo.py and redo-ifchange.py, I suppose.
This commit is contained in:
Avery Pennarun 2010-11-13 00:45:49 -08:00
commit c57de820fb
12 changed files with 103 additions and 87 deletions

27
libdo.py Normal file
View file

@ -0,0 +1,27 @@
import sys, os
import vars
def relpath(t, base):
t = os.path.abspath(t)
tparts = t.split('/')
bparts = base.split('/')
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)
return '/'.join(tparts)
def sname(typ, t):
# FIXME: t.replace(...) is non-reversible and non-unique here!
tnew = relpath(t, vars.BASE)
#log('sname: (%r) %r -> %r\n' % (vars.BASE, t, tnew))
return vars.BASE + ('/.redo/%s^%s' % (typ, tnew.replace('/', '^')))
def add_dep(t, mode, dep):
open(sname('dep', t), 'a').write('%s %s\n' % (mode, dep))

1
redo-ifchange Symbolic link
View file

@ -0,0 +1 @@
redo-ifchange.py

61
redo-ifchange.py Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/python
import sys, os
import vars
from helpers import *
from log import *
from libdo import *
def _dirty_deps(t, depth):
debug('%s?%s\n' % (depth, t))
if not os.path.exists(sname('stamp', t)):
debug('%s-- DIRTY (no stamp)\n' % depth)
return True
stamptime = os.stat(sname('stamp', 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(sname('dep', 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(sname('stamp', t)) # short circuit future checks
return True
return False
if not vars.TARGET:
sys.stderr.write('redo-ifchange: error: must be run from inside a .do\n')
sys.exit(1)
want_build = []
for t in sys.argv[1:]:
mkdirp('%s/.redo' % vars.BASE)
add_dep(vars.TARGET, 'm', t)
if dirty_deps(t, depth = ''):
want_build.append(t)
if want_build:
os.execvp('redo', ['redo', '--'] + want_build)

83
redo.py
View file

@ -8,8 +8,6 @@ redo [targets...]
-- --
d,debug print dependency checks as they happen d,debug print dependency checks as they happen
v,verbose print commands as they are run v,verbose print commands as they are run
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:])
@ -17,90 +15,24 @@ o = options.Options('redo', optspec)
targets = extra or ['it'] targets = extra or ['it']
def relpath(t, base):
t = os.path.abspath(t)
tparts = t.split('/')
bparts = base.split('/')
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)
return '/'.join(tparts)
def sname(typ, t):
# FIXME: t.replace(...) is non-reversible and non-unique here!
tnew = relpath(t, vars.BASE)
#log('sname: (%r) %r -> %r\n' % (vars.BASE, t, tnew))
return vars.BASE + ('/.redo/%s^%s' % (typ, tnew.replace('/', '^')))
def add_dep(t, mode, dep):
open(sname('dep', t), 'a').write('%s %s\n' % (mode, dep))
def find_do_file(t): def find_do_file(t):
dofile = '%s.do' % t dofile = '%s.do' % t
if os.path.exists(dofile): if os.path.exists(dofile):
add_dep(t, 'm', dofile) add_dep(t, 'm', dofile)
if dirty_deps(dofile, depth = ''):
build(dofile)
return dofile return dofile
else: else:
add_dep(t, 'c', dofile) add_dep(t, 'c', dofile)
return None return None
def _dirty_deps(t, depth):
debug('%s?%s\n' % (depth, t))
if not os.path.exists(sname('stamp', t)):
debug('%s-- DIRTY (no stamp)\n' % depth)
return True
stamptime = os.stat(sname('stamp', 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(sname('dep', 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(sname('stamp', t)) # short circuit future checks
return True
return False
def stamp(t): def stamp(t):
stampfile = sname('stamp', t) stampfile = sname('stamp', t)
depfile = sname('dep', t)
if not os.path.exists(vars.BASE + '/.redo'): if not os.path.exists(vars.BASE + '/.redo'):
# .redo might not exist in a 'make clean' target # .redo might not exist in a 'make clean' target
return return
open(stampfile, 'w').close() open(stampfile, 'w').close()
open(depfile, 'a').close()
try: try:
mtime = os.stat(t).st_mtime mtime = os.stat(t).st_mtime
except OSError: except OSError:
@ -126,6 +58,7 @@ def build(t):
return # success return # success
else: else:
raise Exception('no rule to make %r' % t) raise Exception('no rule to make %r' % t)
stamp(dofile)
unlink(t) unlink(t)
tmpname = '%s.redo.tmp' % t tmpname = '%s.redo.tmp' % t
unlink(tmpname) unlink(tmpname)
@ -158,7 +91,6 @@ if opt.debug:
os.environ['REDO_DEBUG'] = '1' os.environ['REDO_DEBUG'] = '1'
if opt.verbose: if opt.verbose:
os.environ['REDO_VERBOSE'] = '1' os.environ['REDO_VERBOSE'] = '1'
assert(not (opt.ifchange and opt.ifcreate))
if not os.environ.get('REDO_BASE', ''): if not os.environ.get('REDO_BASE', ''):
base = os.path.commonprefix([os.path.abspath(os.path.dirname(t)) base = os.path.commonprefix([os.path.abspath(os.path.dirname(t))
@ -173,6 +105,7 @@ if not os.environ.get('REDO_BASE', ''):
import vars import vars
from log import * from log import *
from libdo import *
if not vars.DEPTH: if not vars.DEPTH:
# toplevel call to redo # toplevel call to redo
@ -188,11 +121,5 @@ for t in targets:
os.chdir(startdir) os.chdir(startdir)
if vars.TARGET: if vars.TARGET:
add_dep(vars.TARGET, opt.ifcreate and 'c' or 'm', t) add_dep(vars.TARGET, '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) build(t)

View file

@ -1,2 +1,2 @@
redo --ifchange LD yellow.o redo-ifchange LD yellow.o
./LD bellow yellow.o ./LD bellow yellow.o

View file

@ -1,2 +1,2 @@
redo --ifchange LD hello.o redo-ifchange LD hello.o
./LD hello hello.o ./LD hello hello.o

View file

@ -1,2 +1,2 @@
redo --ifchange CC hello.c /usr/include/stdio.h redo-ifchange CC hello.c /usr/include/stdio.h
./CC hello.c ./CC hello.c

View file

@ -1,2 +1,2 @@
redo --ifchange hello yellow bellow redo-ifchange hello yellow bellow

View file

@ -1,2 +1,2 @@
redo --ifchange it redo-ifchange it
./hello >&2 ./hello >&2

View file

@ -1,2 +1,2 @@
redo --ifchange LD yellow.o redo-ifchange LD yellow.o
./LD yellow yellow.o ./LD yellow yellow.o

View file

@ -1,2 +1,2 @@
redo --ifchange CC hello.c redo-ifchange CC hello.c
gcc -o $3 -c hello.c gcc -o $3 -c hello.c

View file

@ -1,4 +1,4 @@
redo --ifchange t/it redo-ifchange t/it
wvtestrun redo runtests >&2 wvtestrun redo runtests >&2