Remove old, same-named files from a build directory if the file in the source directo...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 8 Oct 2005 17:25:47 +0000 (17:25 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 8 Oct 2005 17:25:47 +0000 (17:25 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1364 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
test/BuildDir/removed-files.py [new file with mode: 0644]

index 0d1cf5f2a1fac7116d820535b96363debba41ae4..902b915940c86c6f9b5446b325c7a1ab8dc8902b 100644 (file)
@@ -396,6 +396,10 @@ RELEASE 0.97 - XXX
   - Fix the -n option when used with -c to print all of the targets
     that will be removed for a multi-target Builder call.
 
+  - If there's no file in the source directory, make sure there isn't
+    one in the build directory, too, to avoid dangling files left
+    over from previous runs when a source file is removed.
+
   From Georg Mischler:
 
   - Remove the space after the -o option when invoking the Borland
index ffdc1eba868489f78db342e953dce71b644cfa86..ceec69656c7c40e1cba1349f2ecc481b1ae7adf0 100644 (file)
@@ -1866,12 +1866,24 @@ class File(Base):
         "__cacheable__"
         # Duplicate from source path if we are set up to do this.
         if self.duplicate and not self.is_derived() and not self.linked:
-            src=self.srcnode()
+            src = self.srcnode()
             if src is self:
                 return Base.exists(self)
+            # At this point, src is meant to be copied in a build directory.
             src = src.rfile()
-            if src.abspath != self.abspath and src.exists():
-                self.do_duplicate(src)
+            if src.abspath != self.abspath:
+                if src.exists():
+                    self.do_duplicate(src)
+                    # Can't return 1 here because the duplication might
+                    # not actually occur if the -n option is being used.
+                else:
+                    # The source file does not exist.  Make sure no old
+                    # copy remains in the build directory.
+                    if Base.exists(self) or self.islink():
+                        self.fs.unlink(self.path)
+                    # Return None explicitly because the Base.exists() call
+                    # above will have cached its value if the file existed.
+                    return None
         return Base.exists(self)
 
     #
index 60b0197f35268b5aa9c61c54bd3674f3416e219e..71fb3ef84372502d1564abf969fc762c1a4dcffe 100644 (file)
@@ -1836,6 +1836,37 @@ class FileTestCase(_tempdirTestCase):
         dirs = fff.Dirs(['d1', 'd2'])
         assert dirs == [d1, d2], map(str, dirs)
 
+    def test_exists(self):
+        """Test the File.exists() method"""
+        fs = self.fs
+        test = self.test
+
+        src_f1 = fs.File('src/f1')
+        assert not src_f1.exists(), "%s apparently exists?" % src_f1
+
+        test.subdir('src')
+        test.write(['src', 'f1'], "src/f1\n")
+
+        assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
+        src_f1.clear()
+        assert src_f1.exists(), "%s apparently does not exist?" % src_f1
+
+        test.subdir('build')
+        fs.BuildDir('build', 'src')
+        build_f1 = fs.File('build/f1')
+
+        assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
+        assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
+
+        test.unlink(['src', 'f1'])
+        src_f1.clear()  # so the next exists() call will look on disk again
+
+        assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
+        build_f1.clear()
+        build_f1.linked = None
+        assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
+        assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
+
 
 
 class RepositoryTestCase(_tempdirTestCase):
diff --git a/test/BuildDir/removed-files.py b/test/BuildDir/removed-files.py
new file mode 100644 (file)
index 0000000..7582a93
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test BuildDir handling of removal of source files.
+
+A C++ Program is created and compiled. First, a header is missing. Then
+the header is added and the compilation should succeed, then the header
+is removed and the compilation should fail again.
+
+Previous versions of SCons did not remove the header from the build
+directory after the source directory's header was removed, which caused
+the compilation to succeed even after the source file was removed.
+
+Test case supplied by Patrick Mezard--many thanks.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+#-------------------------------------------------------------------------------
+#1- Create dep.cpp and the SConstruct. dep.h is missing and the build is
+#expected to fail with 2.
+#-------------------------------------------------------------------------------
+
+test.subdir('src')
+
+test.write(['src', 'dep.cpp'], """\
+#include "dep.h"
+
+int main(int argc, char* argv[])
+{
+       return test_dep();
+}
+""")
+
+test.write('SConstruct', """
+env = Environment()
+env.BuildDir('bin', 'src')
+o = env.Object('bin/dep', 'bin/dep.cpp')
+env.Program('bin/dep', o)
+""")
+
+test.run(arguments = '.', stderr=None, status=2)
+
+
+#-------------------------------------------------------------------------------
+#2- Add dep.h and check the build is OK.
+#-------------------------------------------------------------------------------
+
+test.write(['src', 'dep.h'], """\
+#ifndef DEP_H
+#define DEP_H
+
+inline int test_dep()
+{
+       return 1;
+}
+
+#endif //DEP_H
+""")
+
+test.run(arguments = '.')
+
+
+#-------------------------------------------------------------------------------
+#3- Remove dep.h. The build is expected to fail again like in [1].
+#-------------------------------------------------------------------------------
+
+test.unlink(['src', 'dep.h'])
+
+test.run(arguments = '.', stderr=None, status=2)
+
+
+test.pass_test()