From 4308c54fcc28dd8cfb1bc87f533f99f4037f67b9 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sun, 22 Dec 2002 19:17:19 +0000 Subject: [PATCH] Support Repositories on a different file system, when hard links to the local directory won't work. (Derrick 'dman' Hudson) git-svn-id: http://scons.tigris.org/svn/scons/trunk@528 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/CHANGES.txt | 5 ++ src/engine/SCons/Node/FS.py | 26 +++++---- src/engine/SCons/Node/FSTests.py | 96 ++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 12 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 6edfe9d1..3869feb0 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 4ed46649..5c01c9d6 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 b5c96f94..43fa41fc 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): -- 2.26.2