diff --git a/builder.py b/builder.py index 3d942a7..109fda5 100644 --- a/builder.py +++ b/builder.py @@ -1,32 +1,48 @@ import sys, os, errno, stat import vars, jwack, state -from helpers import unlink, close_on_exec +from helpers import unlink, close_on_exec, join from log import log, log_, debug, debug2, err, warn -def _possible_do_files(t): - t = os.path.join(vars.BASE, t) - yield "%s.do" % t, t, '' - dirname,filename = os.path.split(t) +def _default_do_files(filename): l = filename.split('.') - l[0] = os.path.join(dirname, l[0]) for i in range(1,len(l)+1): - basename = '.'.join(l[:i]) - ext = '.'.join(l[i:]) + basename = join('.', l[:i]) + ext = join('.', l[i:]) if ext: ext = '.' + ext - yield (os.path.join(dirname, "default%s.do" % ext), - os.path.join(dirname, basename), ext) + yield ("default%s.do" % ext), basename, ext + +def _possible_do_files(t): + dirname,filename = os.path.split(t) + yield os.path.join(vars.BASE, dirname), "%s.do" % filename, filename, '' + + # It's important to try every possibility in a directory before resorting + # to a parent directory. Think about nested projects: I don't want + # ../../default.o.do to take precedence over ../default.do, because + # the former one might just be an artifact of someone embedding my project + # into theirs as a subdir. When they do, my rules should still be used + # for building my project in *all* cases. + t = os.path.normpath(os.path.join(vars.BASE, t)) + dirname,filename = os.path.split(t) + dirbits = dirname.split('/') + for i in range(len(dirbits), -1, -1): + basedir = join('/', dirbits[:i]) + subdir = join('/', dirbits[i:]) + for dofile,basename,ext in _default_do_files(filename): + yield basedir, dofile, os.path.join(subdir, basename), ext + def _find_do_file(f): - for dofile,basename,ext in _possible_do_files(f.name): - debug2('%s: %s ?\n' % (f.name, dofile)) - if os.path.exists(dofile): - f.add_dep('m', dofile) - return dofile,basename,ext + for dodir,dofile,basename,ext in _possible_do_files(f.name): + dopath = os.path.join(dodir, dofile) + debug2('%s: %s:%s ?\n' % (f.name, dodir, dofile)) + if os.path.exists(dopath): + f.add_dep('m', dopath) + return dodir,dofile,basename,ext else: - f.add_dep('c', dofile) - return None,None,None + f.add_dep('c', dopath) + return None,None,None,None def _nice(t): @@ -104,7 +120,7 @@ class BuildJob: sf.save() return self._after2(0) sf.zap_deps1() - (dofile, basename, ext) = _find_do_file(sf) + (dodir, dofile, basename, ext) = _find_do_file(sf) if not dofile: if os.path.exists(t): sf.set_static() @@ -120,8 +136,8 @@ class BuildJob: self.f = os.fdopen(ffd, 'w+') # this will run in the dofile's directory, so use only basenames here argv = ['sh', '-e', - os.path.basename(dofile), - os.path.basename(basename), # target name (extension removed) + dofile, + basename, # target name (no extension) ext, # extension (if any), including leading dot os.path.basename(self.tmpname2) # randomized output file name ] @@ -129,6 +145,9 @@ class BuildJob: if vars.XTRACE: argv[1] += 'x' if vars.VERBOSE or vars.XTRACE: log_('\n') log('%s\n' % _nice(t)) + self.dodir = dodir + self.basename = basename + self.ext = ext self.argv = argv sf.is_generated = True sf.save() @@ -168,10 +187,10 @@ class BuildJob: # redo-ifchange, and it might have done it from a different directory # than we started it in. So os.getcwd() might be != REDO_PWD right # now. - dn = os.path.dirname(self.t) + dn = self.dodir newp = os.path.realpath(dn) os.environ['REDO_PWD'] = state.relpath(newp, vars.STARTDIR) - os.environ['REDO_TARGET'] = os.path.basename(self.t) + os.environ['REDO_TARGET'] = self.basename + self.ext os.environ['REDO_DEPTH'] = vars.DEPTH + ' ' if dn: os.chdir(dn) diff --git a/state.py b/state.py index 37363ea..7798c70 100644 --- a/state.py +++ b/state.py @@ -261,7 +261,7 @@ class File(object): def add_dep(self, mode, dep): src = File(name=dep) - debug2('add-dep: %r < %s %r\n' % (self.name, mode, src.name)) + debug3('add-dep: "%s" < %s "%s"\n' % (self.name, mode, src.name)) assert(self.id != src.id) _write("insert or replace into Deps " " (target, mode, source, delete_me) values (?,?,?,?)", diff --git a/t/.gitignore b/t/.gitignore index 60b3c7a..8b498e2 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,9 +1,3 @@ -c -c.c -c.c.c -c.c.c.b -c.c.c.b.b -d test.args test2.args /passfail diff --git a/t/all.do b/t/all.do index b15b1aa..bc46f72 100644 --- a/t/all.do +++ b/t/all.do @@ -1,2 +1,2 @@ -redo-ifchange hello yellow bellow defaults-flat/all example/all - +redo-ifchange hello yellow bellow defaults-flat/all defaults-nested/all \ + example/all diff --git a/t/clean.do b/t/clean.do index 3062fb3..5420ba8 100644 --- a/t/clean.do +++ b/t/clean.do @@ -1,5 +1,5 @@ redo example/clean curse/clean deps/clean "space dir/clean" stamp/clean \ - defaults-flat/clean + defaults-flat/clean defaults-nested/clean rm -f mode1 makedir.log chdir1 deltest2 \ hello [by]ellow *.o *~ .*~ *.log CC LD passfail silence silence.do \ touch1 touch1.do always1 ifcreate[12].dep ifcreate[12] diff --git a/t/defaults-flat/.gitignore b/t/defaults-flat/.gitignore new file mode 100644 index 0000000..580e8c9 --- /dev/null +++ b/t/defaults-flat/.gitignore @@ -0,0 +1,6 @@ +c +c.c +c.c.c +c.c.c.b +c.c.c.b.b +d diff --git a/t/defaults-nested/.gitignore b/t/defaults-nested/.gitignore new file mode 100644 index 0000000..b656a9f --- /dev/null +++ b/t/defaults-nested/.gitignore @@ -0,0 +1,15 @@ +/a/b/file +/a/b/file.x.y.z +/a/b/file.y.z +/a/b/file.z +/a/d/file +/a/d/file.x.y.z +/a/d/file.y.z +/a/d/file.z +/a/file +/a/file.x.y.z +/a/file.y.z +/a/file.z +/file.x.y.z +/file.z +/file diff --git a/t/defaults-nested/a/b/default.y.z.do b/t/defaults-nested/a/b/default.y.z.do new file mode 100644 index 0000000..648b3bb --- /dev/null +++ b/t/defaults-nested/a/b/default.y.z.do @@ -0,0 +1 @@ +echo default.y.z $1 $2 diff --git a/t/defaults-nested/a/b/file.x.y.z.do b/t/defaults-nested/a/b/file.x.y.z.do new file mode 100644 index 0000000..d11e86a --- /dev/null +++ b/t/defaults-nested/a/b/file.x.y.z.do @@ -0,0 +1 @@ +echo file $1 $2 diff --git a/t/defaults-nested/a/d/default.do b/t/defaults-nested/a/d/default.do new file mode 100644 index 0000000..162e667 --- /dev/null +++ b/t/defaults-nested/a/d/default.do @@ -0,0 +1 @@ +echo default $1 $2 diff --git a/t/defaults-nested/a/default.x.y.z.do b/t/defaults-nested/a/default.x.y.z.do new file mode 100644 index 0000000..3b0398e --- /dev/null +++ b/t/defaults-nested/a/default.x.y.z.do @@ -0,0 +1,2 @@ +echo default.x.y.z $1 $2 + diff --git a/t/defaults-nested/a/default.z.do b/t/defaults-nested/a/default.z.do new file mode 100644 index 0000000..02b313a --- /dev/null +++ b/t/defaults-nested/a/default.z.do @@ -0,0 +1 @@ +echo default.z $1 $2 diff --git a/t/defaults-nested/all.do b/t/defaults-nested/all.do new file mode 100644 index 0000000..7b789e5 --- /dev/null +++ b/t/defaults-nested/all.do @@ -0,0 +1 @@ +redo test diff --git a/t/defaults-nested/clean.do b/t/defaults-nested/clean.do new file mode 100644 index 0000000..5a76b40 --- /dev/null +++ b/t/defaults-nested/clean.do @@ -0,0 +1,6 @@ +exec >&2 +find -name '*~' -exec rm -f {} \; +rm -f a/b/file a/b/file.x.y.z a/b/file.y.z a/b/file.z \ + a/d/file a/d/file.x.y.z a/d/file.y.z a/d/file.z \ + a/file a/file.x.y.z a/file.y.z a/file.z \ + file.x.y.z file.z file diff --git a/t/defaults-nested/default.do b/t/defaults-nested/default.do new file mode 100644 index 0000000..790be20 --- /dev/null +++ b/t/defaults-nested/default.do @@ -0,0 +1,2 @@ +echo root $1 $2 + diff --git a/t/defaults-nested/test.do b/t/defaults-nested/test.do new file mode 100644 index 0000000..9ddcb7d --- /dev/null +++ b/t/defaults-nested/test.do @@ -0,0 +1,36 @@ +exec >&2 +redo-ifchange \ + file.x.y.z file.z file \ + a/b/file.x.y.z a/b/file.y.z a/b/file.z a/b/file \ + a/d/file.x.y.z a/d/file.y.z a/d/file.z a/d/file + +(cd a/b && redo-ifchange ../file.x.y.z ../file.y.z ../file.z ../file) + +check() +{ + if [ "$(cat $1)" != "$2" ]; then + echo "$1 should contain '$2'" + echo " ...got '$(cat $1)'" + exit 44 + fi +} + +check file.x.y.z "root file.x.y.z" +check file.z "root file.z" +check file "root file" + +check a/file.x.y.z "default.x.y.z file .x.y.z" +check a/file.y.z "default.z file.y .z" +check a/file.z "default.z file .z" +check a/file "root a/file" + +check a/b/file.x.y.z "file file.x.y.z" +check a/b/file.y.z "default.y.z file .y.z" +check a/b/file.z "default.z b/file .z" +check a/b/file "root a/b/file" + +check a/d/file.x.y.z "default file.x.y.z" +check a/d/file.y.z "default file.y.z" +check a/d/file.z "default file.z" +check a/d/file "default file" +