diff options
author | Mats Wichmann <mats@linux.com> | 2022-03-21 15:45:55 (GMT) |
---|---|---|
committer | Mats Wichmann <mats@linux.com> | 2022-05-06 12:48:09 (GMT) |
commit | 769edf8d42ef2c05ac5e9c768f1d9e8f3cedbfcf (patch) | |
tree | a2daa46094184d99d293878787b973d9f693379c /SCons/Node | |
parent | 3e7b8ad568dfbfec9c92d59ef5276b1975725ce4 (diff) | |
download | SCons-769edf8d42ef2c05ac5e9c768f1d9e8f3cedbfcf.zip SCons-769edf8d42ef2c05ac5e9c768f1d9e8f3cedbfcf.tar.gz SCons-769edf8d42ef2c05ac5e9c768f1d9e8f3cedbfcf.tar.bz2 |
Fix content-timestamp decider for symlinks
The base filesystem node class has getmtime() and getsize() functions.
Those were changed in an early commit to use the lstat() method if
the node represented a symbolic link. However, we actually want the
information of the file the symlink points to, or we can't detect
changes to the mtime or size of the underlying file, and miss rebuilds
if content-timestamp is used.
Added a testcase which shows the failure to rebuild from the symlinked
source.
Fixes #3880
Signed-off-by: Mats Wichmann <mats@linux.com>
Diffstat (limited to 'SCons/Node')
-rw-r--r-- | SCons/Node/FS.py | 24 | ||||
-rw-r--r-- | SCons/Node/FSTests.py | 12 |
2 files changed, 15 insertions, 21 deletions
diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py index c1bfd6a..a7e25b9 100644 --- a/SCons/Node/FS.py +++ b/SCons/Node/FS.py @@ -255,7 +255,7 @@ else: def _copy_func(fs, src, dest): shutil.copy2(src, dest) st = fs.stat(src) - fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + fs.chmod(dest, stat.S_IMODE(st.st_mode) | stat.S_IWRITE) Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', @@ -733,39 +733,33 @@ class Base(SCons.Node.Node): return SCons.Node._rexists_map[self._func_rexists](self) def getmtime(self): - if self.islink(): - st = self.lstat() - else: - st = self.stat() + st = self.stat() if st: - return st[stat.ST_MTIME] + return st.st_mtime else: return None def getsize(self): - if self.islink(): - st = self.lstat() - else: - st = self.stat() + st = self.stat() if st: - return st[stat.ST_SIZE] + return st.st_size else: return None def isdir(self): st = self.stat() - return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) + return st is not None and stat.S_ISDIR(st.st_mode) def isfile(self): st = self.stat() - return st is not None and stat.S_ISREG(st[stat.ST_MODE]) + return st is not None and stat.S_ISREG(st.st_mode) if hasattr(os, 'symlink'): def islink(self): st = self.lstat() - return st is not None and stat.S_ISLNK(st[stat.ST_MODE]) + return st is not None and stat.S_ISLNK(st.st_mode) else: def islink(self): return False # no symlinks @@ -2793,7 +2787,7 @@ class File(Base): return size @SCons.Memoize.CountMethodCall - def get_timestamp(self) -> int: + def get_timestamp(self) -> float: try: return self._memo['get_timestamp'] except KeyError: diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index b93efc8..da606e2 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -408,8 +408,8 @@ class VariantDirTestCase(unittest.TestCase): None) os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE) st = os.stat(test.workpath('build/foo')) - assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \ - stat.S_IMODE(st[stat.ST_MODE]) + assert (stat.S_IMODE(st.st_mode) & stat.S_IWRITE), \ + stat.S_IMODE(st.st_mode) # This used to generate a UserError when we forbid the source # directory from being outside the top-level SConstruct dir. @@ -769,9 +769,9 @@ class FileNodeInfoTestCase(_tempdirTestCase): ni.update(fff) - mtime = st[stat.ST_MTIME] + mtime = st.st_mtime assert ni.timestamp == mtime, (ni.timestamp, mtime) - size = st[stat.ST_SIZE] + size = st.st_size assert ni.size == size, (ni.size, size) import time @@ -781,9 +781,9 @@ class FileNodeInfoTestCase(_tempdirTestCase): st = os.stat('fff') - mtime = st[stat.ST_MTIME] + mtime = st.st_mtime assert ni.timestamp != mtime, (ni.timestamp, mtime) - size = st[stat.ST_SIZE] + size = st.st_size assert ni.size != size, (ni.size, size) |