redo-whichdo: fix a bug where the last dir was checked twice, and add tests.

When we can't find a .do file, we walk all the way back to the root
directory.  When that happens, the root directory is actually searched
twice.  This is harmless (since a .do file doesn't exist there anyway)
but causes redo-whichdo to produce the wrong output.

Also, add a test, which I forgot to do when writing whichdo in the
first place.

To make the test work from the root directory, we need a way to
initialize redo without actually creating a .redo directory.  Add a
init_no_state() function for that purpose, and split the necessary path
functions into their own module so we can avoid importing builder.py.
This commit is contained in:
Avery Pennarun 2018-10-30 23:23:04 -04:00
commit e40dc5bad2
10 changed files with 133 additions and 50 deletions

48
paths.py Normal file
View file

@ -0,0 +1,48 @@
import os
import vars
from log import err, debug2
def _default_do_files(filename):
l = filename.split('.')
for i in range(1,len(l)+1):
basename = '.'.join(l[:i])
ext = '.'.join(l[i:])
if ext: ext = '.' + 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: We 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('/')
# since t is an absolute path, dirbits[0] is always '', so we don't
# need to count all the way down to i=0.
for i in range(len(dirbits), 0, -1):
basedir = '/'.join(dirbits[:i])
subdir = '/'.join(dirbits[i:])
for dofile,basename,ext in _default_do_files(filename):
yield (basedir, dofile,
subdir, os.path.join(subdir, basename), ext)
def find_do_file(f):
for dodir,dofile,basedir,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,basedir,basename,ext
else:
f.add_dep('c', dopath)
return None,None,None,None,None