summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-12-22 19:17:19 (GMT)
committerSteven Knight <knight@baldmt.com>2002-12-22 19:17:19 (GMT)
commited5697feb65d129a082d59408ddfcfac4ee2134b (patch)
treea54fddb1c55f55b94863e851688a3a80ce6f3e38
parent28a061162bbfc8f889e09aab3c4c4282860b1a7a (diff)
downloadSCons-ed5697feb65d129a082d59408ddfcfac4ee2134b.zip
SCons-ed5697feb65d129a082d59408ddfcfac4ee2134b.tar.gz
SCons-ed5697feb65d129a082d59408ddfcfac4ee2134b.tar.bz2
Support Repositories on a different file system, when hard links to the local directory won't work. (Derrick 'dman' Hudson)
-rw-r--r--src/CHANGES.txt5
-rw-r--r--src/engine/SCons/Node/FS.py26
-rw-r--r--src/engine/SCons/Node/FSTests.py96
3 files changed, 115 insertions, 12 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 6edfe9d..3869feb 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -10,6 +10,11 @@
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.
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 4ed4664..5c01c9d 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -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:
"""
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index b5c96f9..43fa41f 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -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):