summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-03-06 14:09:36 (GMT)
committerSteven Knight <knight@baldmt.com>2004-03-06 14:09:36 (GMT)
commit10e229b7a8e466d9fe1d193fa66a2bcd3fec3ee6 (patch)
tree1eaf77423e1ed4a4604c215ea2e84384519f2d44 /src/engine
parent0486b66e2cdef2d174ff9eba0198947c4f8a9635 (diff)
downloadSCons-10e229b7a8e466d9fe1d193fa66a2bcd3fec3ee6.zip
SCons-10e229b7a8e466d9fe1d193fa66a2bcd3fec3ee6.tar.gz
SCons-10e229b7a8e466d9fe1d193fa66a2bcd3fec3ee6.tar.bz2
scons.0.92 - Implement a --duplicate= option. (Christoph Wiedemann)
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/SCons/Node/FS.py84
-rw-r--r--src/engine/SCons/Node/FSTests.py132
-rw-r--r--src/engine/SCons/Script/__init__.py27
3 files changed, 163 insertions, 80 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index dbcaf8a..3bf2ee4 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -66,29 +66,77 @@ import SCons.Warnings
# there should be *no* changes to the external file system(s)...
#
+def _copy_func(src, dest):
+ shutil.copy2(src, dest)
+ st=os.stat(src)
+ os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+
+Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy',
+ 'hard-copy', 'soft-copy', 'copy']
+
+Link_Funcs = [] # contains the callables of the specified duplication style
+
+def set_duplicate(duplicate):
+ # Fill in the Link_Funcs list according to the argument
+ # (discarding those not available on the platform).
+
+ # Set up the dictionary that maps the argument names to the
+ # underlying implementations. We do this inside this function,
+ # not in the top-level module code, so that we can remap os.link
+ # and os.symlink for testing purposes.
+ try:
+ _hardlink_func = os.link
+ except AttributeError:
+ _hardlink_func = None
+
+ try:
+ _softlink_func = os.symlink
+ except AttributeError:
+ _softlink_func = None
+
+ link_dict = {
+ 'hard' : _hardlink_func,
+ 'soft' : _softlink_func,
+ 'copy' : _copy_func
+ }
+
+ if not duplicate in Valid_Duplicates:
+ raise SCons.Errors.InternalError, ("The argument of set_duplicate "
+ "should be in Valid_Duplicates")
+ global Link_Funcs
+ Link_Funcs = []
+ for func in string.split(duplicate,'-'):
+ if link_dict[func]:
+ Link_Funcs.append(link_dict[func])
+
def LinkFunc(target, source, env):
- t = target[0]
- dest = t.path
- fs = t.fs
- src = source[0].path
+ # Relative paths cause problems with symbolic links, so
+ # we use absolute paths, which may be a problem for people
+ # who want to move their soft-linked src-trees around. Those
+ # people should use the 'hard-copy' mode, softlinks cannot be
+ # used for that; at least I have no idea how ...
+ src = source[0].abspath
+ dest = target[0].abspath
dir, file = os.path.split(dest)
- if dir and not fs.isdir(dir):
- fs.makedirs(dir)
- # 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 :
- fs.link(src, dest)
- except (AttributeError, OSError):
- try :
- fs.symlink(src, dest)
- except (AttributeError, OSError):
- fs.copy2(src, dest)
- st = fs.stat(src)
- fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ if dir and not os.path.isdir(dir):
+ os.makedirs(dir)
+ if not Link_Funcs:
+ # Set a default order of link functions.
+ set_duplicate('hard-soft-copy')
+ # Now link the files with the previously specified order.
+ for func in Link_Funcs:
+ try:
+ func(src,dest)
+ break
+ except OSError:
+ if func == Link_Funcs[-1]:
+ # exception of the last link method (copy) are fatal
+ raise
+ else:
+ pass
return 0
Link = SCons.Action.Action(LinkFunc, None)
-
def LocalString(target, source, env):
return 'Local copy of %s from %s' % (target[0], source[0])
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 85ae4d9..63ac29d 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -413,15 +413,14 @@ class BuildDirTestCase(unittest.TestCase):
class LinkSimulator :
"""A class to intercept os.[sym]link() calls and track them."""
- def __init__( self ) :
+ def __init__( self, duplicate ) :
+ self.duplicate = duplicate
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
+ self.links_to_be_called = self.duplicate
def _need_reset( self ) :
"""
@@ -430,85 +429,98 @@ class BuildDirTestCase(unittest.TestCase):
or if all three methods have been tried already.
"""
return (
- ( not hasattr( self , "link_called" ) )
+ ( not hasattr( self , "links_to_be_called" ) )
or
- ( self.link_called and
- self.symlink_called and
- self.copy_called )
+ (self.links_to_be_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
+ l = string.split(self.links_to_be_called, "-")
+ next_link = l[0]
+ assert next_link == "hard", \
+ "Wrong link order: expected %s to be called "\
+ "instead of hard" % next_link
+ self.links_to_be_called = string.join(l[1:], '-')
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
+ l = string.split(self.links_to_be_called, "-")
+ next_link = l[0]
+ assert next_link == "soft", \
+ "Wrong link order: expected %s to be called "\
+ "instead of soft" % next_link
+ self.links_to_be_called = string.join(l[1:], '-')
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
+ l = string.split(self.links_to_be_called, "-")
+ next_link = l[0]
+ assert next_link == "copy", \
+ "Wrong link order: expected %s to be called "\
+ "instead of copy" % next_link
+ self.links_to_be_called = string.join(l[1:], '-')
# 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 = None
- real_symlink = None
try:
- real_link = os.link
- except AttributeError:
- pass
- try:
- real_symlink = os.symlink
- except AttributeError:
+ SCons.Node.FS.set_duplicate("no-link-order")
+ assert 0, "Expected exception when passing an invalid duplicate to set_duplicate"
+ except SCons.Errors.InternalError:
pass
- real_copy = shutil.copy2
- simulator._real_copy = real_copy # the simulator needs the real one
+
+ for duplicate in SCons.Node.FS.Valid_Duplicates:
+ simulator = LinkSimulator(duplicate)
- # override the real functions with our simulation
- os.link = simulator.link_fail
- os.symlink = simulator.symlink_fail
- shutil.copy2 = simulator.copy
+ # save the real functions for later restoration
+ real_link = None
+ real_symlink = None
+ try:
+ real_link = os.link
+ except AttributeError:
+ pass
+ try:
+ real_symlink = os.symlink
+ except AttributeError:
+ pass
+ 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
+ SCons.Node.FS.set_duplicate(duplicate)
+
+ src_foo = test.workpath('src', 'foo')
+ build_foo = test.workpath('build', 'foo')
- try:
- test.write('src/foo', 'src/foo\n')
try:
- os.chmod(test.workpath('src/foo'), stat.S_IRUSR)
- SCons.Node.FS.Link(fs.File(test.workpath('build/foo')),
- fs.File(test.workpath('src/foo')),
- None)
- os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE)
- finally:
- test.unlink( "src/foo" )
- test.unlink( "build/foo" )
+ test.write(src_foo, 'src/foo\n')
+ os.chmod(src_foo, stat.S_IRUSR)
+ try:
+ SCons.Node.FS.Link(fs.File(build_foo),
+ fs.File(src_foo),
+ None)
+ finally:
+ os.chmod(src_foo, stat.S_IRUSR | stat.S_IWRITE)
+ test.unlink(src_foo)
+ test.unlink(build_foo)
- finally:
- # restore the real functions
- if real_link:
- os.link = real_link
- else:
- delattr(os, 'link')
- if real_symlink:
- os.symlink = real_symlink
- else:
- delattr(os, 'symlink')
- shutil.copy2 = real_copy
+ finally:
+ # restore the real functions
+ if real_link:
+ os.link = real_link
+ else:
+ delattr(os, 'link')
+ if real_symlink:
+ os.symlink = real_symlink
+ else:
+ delattr(os, 'symlink')
+ shutil.copy2 = real_copy
class FSTestCase(unittest.TestCase):
def runTest(self):
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 7067ab1..47fef2b 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -608,6 +608,17 @@ class OptParser(OptionParser):
# "LOAD-AVERAGE."
# type="int",
help=SUPPRESS_HELP)
+ def opt_duplicate(option, opt, value, parser):
+ if not value in SCons.Node.FS.Valid_Duplicates:
+ raise OptionValueError("`%s' is not a valid duplication style." % value)
+ setattr(parser.values, 'duplicate', value)
+ # Set the duplicate stye right away so it can affect linking
+ # of SConscript files.
+ SCons.Node.FS.set_duplicate(value)
+ self.add_option('--duplicate', action="callback", type="string",
+ callback=opt_duplicate, nargs=1, dest="duplicate",
+ help="Set the preferred duplication methods. Must be one of "
+ + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
self.add_option('--list-derived', action="callback",
callback=opt_not_yet,
# help="Don't build; list files that would be built."
@@ -683,7 +694,8 @@ class SConscriptSettableOptions:
self.settable = {'num_jobs':1,
'max_drift':SCons.Sig.default_max_drift,
'implicit_cache':0,
- 'clean':0}
+ 'clean':0,
+ 'duplicate':'hard-soft-copy'}
def get(self, name):
if not self.settable.has_key(name):
@@ -709,6 +721,16 @@ class SConscriptSettableOptions:
value = int(value)
except ValueError, x:
raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
+ elif name == 'duplicate':
+ try:
+ value = str(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
+ if not value in SCons.Node.FS.Valid_Duplicates:
+ raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
+ # Set the duplicate stye right away so it can affect linking
+ # of SConscript files.
+ SCons.Node.FS.set_duplicate(value)
self.settable[name] = value
@@ -745,7 +767,7 @@ def _main(args, parser):
CleanTask.execute = CleanTask.show
if options.question:
SCons.SConf.dryrun = 1
-
+
if options.no_progress or options.silent:
progress_display.set_mode(0)
if options.silent:
@@ -877,6 +899,7 @@ def _main(args, parser):
# Now that we've read the SConscripts we can set the options
# that are SConscript settable:
SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
+ SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
lookup_top = None
if targets: