Support Repositories on a different file system, when hard links to the local directo...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 22 Dec 2002 19:17:19 +0000 (19:17 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 22 Dec 2002 19:17:19 +0000 (19:17 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@528 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py

index 6edfe9d1613a69c6c0ec0488d29fcee0f11459ed..3869feb0a61c9e3dc13ed0985654a2b7956f9b37 100644 (file)
 
 RELEASE 0.10 - XXX
 
+  From Derrick 'dman' Hudson:
+
+  - Support Repositories on other file systems by symlinking or
+    copying files when hard linking won't work.
+
   From Steven Knight:
 
   - Remove Python bytecode (*.pyc) files from the scons-local packages.
index 4ed46649f1a40f12e903651ba8d21e8a748602b8..5c01c9d644c0d26e70335f6d885c453b40a1cefb 100644 (file)
@@ -48,22 +48,24 @@ import SCons.Warnings
 
 execute_actions = 1
 
-try:
-    import os
-    _link = os.link
-except AttributeError:
-    import shutil
-    import stat
-    def _link(src, dest):
-        shutil.copy2(src, dest)
-        st=os.stat(src)
-        os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
-
 def file_link(src, dest):
     dir, file = os.path.split(dest)
     if dir and not os.path.isdir(dir):
         os.makedirs(dir)
-    _link(src, dest)
+    # Now actually link the files.  First try to make a hard link.  If that
+    # fails, try a symlink.  If that fails then just copy it.
+    try :
+        os.link(src, dest)
+    except (AttributeError, OSError) :
+        try :
+            os.symlink(src, dest)
+        except (AttributeError, OSError) :
+            import shutil
+            import stat
+            shutil.copy2(src, dest)
+            st=os.stat(src)
+            os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+
 
 class ParentOfRoot:
     """
index b5c96f9472744d29033d3fe725217e49a55c77e3..43fa41fc6c206f4c87b6cd1beda4fd0af9fe4102 100644 (file)
@@ -32,10 +32,18 @@ import unittest
 import SCons.Node.FS
 from TestCmd import TestCmd
 import SCons.Errors
+import shutil
 import stat
 
 built_it = None
 
+# This will be built-in in 2.3.  For now fake it.
+try :
+    True , False
+except NameError :
+    True = 1 ; False = 0
+
+
 class Builder:
     def __init__(self, factory):
         self.factory = factory
@@ -272,6 +280,94 @@ class BuildDirTestCase(unittest.TestCase):
         except SCons.Errors.UserError:
             exc_caught = 1
         assert exc_caught, "Should have caught a UserError."
+        test.unlink( "src/foo" )
+        test.unlink( "build/foo" )
+
+        # verify the link creation attempts in file_link()
+        class LinkSimulator :
+            """A class to intercept os.[sym]link() calls and track them."""
+
+            def __init__( self ) :
+                self._reset()
+
+            def _reset( self ) :
+                """Reset the simulator if necessary"""
+                if not self._need_reset() : return # skip if not needed now
+                self.link_called = False
+                self.symlink_called = False
+                self.copy_called = False
+
+            def _need_reset( self ) :
+                """
+                Determines whether or not the simulator needs to be reset.
+                A reset is necessary if the object is first being initialized,
+                or if all three methods have been tried already.
+                """
+                return (
+                        ( not hasattr( self , "link_called" ) )
+                        or
+                        ( self.link_called and
+                          self.symlink_called and
+                          self.copy_called )
+                       )
+
+            def link_fail( self , src , dest ) :
+                self._reset()
+                assert not self.symlink_called , \
+                        "Wrong link order: symlink tried before hard link."
+                assert not self.copy_called , \
+                        "Wrong link order: copy tried before hard link."
+                self.link_called = True
+                raise OSError( "Simulating hard link creation error." )
+
+            def symlink_fail( self , src , dest ) :
+                self._reset()
+                assert self.link_called , \
+                        "Wrong link order: hard link not tried before symlink."
+                assert not self.copy_called , \
+                        "Wrong link order: copy tried before symlink link."
+                self.symlink_called = True
+                raise OSError( "Simulating symlink creation error." )
+
+            def copy( self , src , dest ) :
+                self._reset()
+                assert self.link_called , \
+                        "Wrong link order: hard link not tried before copy."
+                assert self.symlink_called , \
+                        "Wrong link order: symlink not tried before copy."
+                self.copy_called = True
+                # copy succeeds, but use the real copy
+                self._real_copy(src, dest)
+        # end class LinkSimulator
+        simulator = LinkSimulator()
+
+        # save the real functions for later restoration
+        real_link = os.link
+        real_symlink = os.symlink
+        real_copy = shutil.copy2
+        simulator._real_copy = real_copy # the simulator needs the real one
+
+        # override the real functions with our simulation
+        os.link = simulator.link_fail
+        os.symlink = simulator.symlink_fail
+        shutil.copy2 = simulator.copy
+
+        # XXX this is just to pass the baseline test, it won't be needed once
+        # this change is integrated
+        SCons.Node.FS._link = simulator.link_fail
+
+        test.write('src/foo', 'foo\n')
+        os.chmod(test.workpath('src/foo'), stat.S_IRUSR)
+        SCons.Node.FS.file_link(test.workpath('src/foo'),
+                                test.workpath('build/foo'))
+        test.unlink( "src/foo" )
+        test.unlink( "build/foo" )
+
+        # restore the real functions
+        os.link = real_link
+        os.symlink = real_symlink
+        shutil.copy2 = real_copy
+
 
 class FSTestCase(unittest.TestCase):
     def runTest(self):