Use os.lstat() instead of os.stat().

I think this aligns better with how redo works.  Otherwise, if a.do
creates a as a symlink, then changes to the symlink's *target* will
change a's stat/stamp information without re-running a.do, which looks
to redo like you modified a by hand, which causes it to stop running
a.do altogether.

With this change, modifications to a's target are okay, but they don't
trigger any redo dependency changes.  If you want that, then a.do
should redo-ifchange on its symlink target explicitly.
This commit is contained in:
Avery Pennarun 2018-10-06 00:14:02 -04:00
commit 34669fba65
8 changed files with 47 additions and 2 deletions

View file

@ -53,7 +53,7 @@ def _nice(t):
def _try_stat(filename):
try:
return os.stat(filename)
return os.lstat(filename)
except OSError, e:
if e.errno == errno.ENOENT:
return None

View file

@ -275,7 +275,7 @@ class File(object):
def read_stamp(self):
try:
st = os.stat(os.path.join(vars.BASE, self.name))
st = os.lstat(os.path.join(vars.BASE, self.name))
except OSError:
return STAMP_MISSING
if stat.S_ISDIR(st.st_mode):

View file

@ -10,4 +10,5 @@ rm -f fail
touch fail
../flush-cache
# since we created this file by hand, fail.do won't run, so it won't fail.
redo-ifchange fail >&/dev/null || exit 55 # expected to pass

4
t/360-symlinks/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.did
*.extra
a
b

4
t/360-symlinks/a.do Normal file
View file

@ -0,0 +1,4 @@
echo x >>a.did
rm -f $2.extra
echo foo >$2.extra
ln -s $2.extra $3

32
t/360-symlinks/all.do Normal file
View file

@ -0,0 +1,32 @@
rm -f a a.extra b b.did
d0=""
redo a
redo-ifchange b
d1=$(cat b.did)
[ "$d0" != "$d1" ] || exit 11
# b only rebuilds if a changes
../flush-cache
redo-ifchange b
d2=$(cat b.did)
[ "$d1" = "$d2" ] || exit 12
# forcibly changing a should rebuild b.
# a is already symlink to a.extra, but redo shouldn't care about the
# target of symlinks, so it shouldn't freak out that a.extra has changed.
# Anyway, b should still rebuild because a was rebuilt.
../flush-cache
redo a
redo-ifchange b
d3=$(cat b.did)
[ "$d2" != "$d3" ] || exit 13
# Explicitly check that changing a's symlink target (a.extra) does *not*
# trigger a rebuild of b, because b depends on the stamp of the symlink,
# not what the symlink points to. In redo, you declare dependencies on
# specific filenames, not the things they happen to refer to.
../flush-cache
touch a.extra
redo-ifchange b
d4=$(cat b.did)
[ "$d3" = "$d4" ] || exit 14

3
t/360-symlinks/b.do Normal file
View file

@ -0,0 +1,3 @@
echo x >>b.did
redo-ifchange a
cat a >$3

1
t/360-symlinks/clean.do Normal file
View file

@ -0,0 +1 @@
rm -f *~ .*~ a b *.extra *.did