diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/engine/SCons/Action.py | 11 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 18 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/EnvironmentTests.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 148 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 17 | ||||
-rw-r--r-- | src/engine/SCons/Util.py | 69 | ||||
-rw-r--r-- | src/engine/SCons/UtilTests.py | 16 |
10 files changed, 175 insertions, 120 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index befac1e..49b0cb1 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -262,8 +262,10 @@ class CommandAction(ActionBase): if not SCons.Util.is_List(cmd): cmd = [ cmd ] return SCons.Util.scons_subst(string.join(map(str, cmd)), - env, SCons.Util.SUBST_RAW, - target, source) + env, + SCons.Util.SUBST_RAW, + SCons.Util.target_prep(target), + SCons.Util.source_prep(source)) def get_contents(self, target, source, env): """Return the signature contents of this action's command line. @@ -277,7 +279,8 @@ class CommandAction(ActionBase): return SCons.Util.scons_subst(string.join(map(str, cmd)), env, SCons.Util.SUBST_SIG, - target, source) + SCons.Util.target_prep(target), + SCons.Util.source_prep(source)) class CommandGeneratorAction(ActionBase): """Class for command-generator actions.""" @@ -403,6 +406,8 @@ class ListAction(ActionBase): Simple concatenation of the signatures of the elements. """ + target = SCons.Util.target_prep(target) + source = SCons.Util.source_prep(source) return string.join(map(lambda x, t=target, s=source, e=env: x.get_contents(t, s, e), self.list), diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 33bf57e..3e32594 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -134,6 +134,8 @@ class DummyNode: return self.name def rfile(self): return self + def get_subst_proxy(self): + return self if os.name == 'java': python = os.path.join(sys.prefix, 'jython') @@ -392,7 +394,7 @@ class CommandActionTestCase(unittest.TestCase): cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile) act = SCons.Action.CommandAction(cmd2) - r = act('foo', [], env.Copy()) + r = act(DummyNode('foo'), [], env.Copy()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'foo'\n", c @@ -400,7 +402,7 @@ class CommandActionTestCase(unittest.TestCase): cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile) act = SCons.Action.CommandAction(cmd3) - r = act(['aaa', 'bbb'], [], env.Copy()) + r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Copy()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'aaa' 'bbb'\n", c @@ -437,13 +439,13 @@ class CommandActionTestCase(unittest.TestCase): PATH = '' env5['ENV']['XYZZY'] = 'xyzzy' - r = act(target = 'out5', source = [], env = env5) + r = act(target = DummyNode('out5'), source = [], env = env5) act = SCons.Action.CommandAction(cmd5) r = act(target = DummyNode('out5'), - source = [], - env = env.Copy(ENV = {'XYZZY' : 'xyzzy', - 'PATH' : PATH})) + source = [], + env = env.Copy(ENV = {'XYZZY' : 'xyzzy', + 'PATH' : PATH})) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c @@ -455,6 +457,8 @@ class CommandActionTestCase(unittest.TestCase): return self._str def rfile(self): return self + def get_subst_proxy(self): + return self cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile) @@ -749,6 +753,8 @@ class CommandGeneratorActionTestCase(unittest.TestCase): def rfile(self): self.t.rfile_called = 1 return self + def get_subst_proxy(self): + return self def f3(target, source, env, for_signature): return '' c = SCons.Action.CommandGeneratorAction(f3) diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 5f9a18c..6def6d5 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -255,6 +255,8 @@ class BuilderTestCase(unittest.TestCase): return self.name def rfile(self): return self + def get_subst_proxy(self): + return self target = map(DummyNode, map(lambda x: "__t%d__" % x, range(1, 7))) source = map(DummyNode, map(lambda x: "__s%d__" % x, range(1, 7))) diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 28ea0e5..6473bdd 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -574,6 +574,8 @@ class EnvironmentTestCase(unittest.TestCase): return self.name def rfile(self): return self + def get_subst_proxy(self): + return self # Test callables in the Environment def foo(target, source, env, for_signature): diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index e64aacc..884736e 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -190,7 +190,7 @@ class ParentOfRoot: This class is an instance of the Null object pattern. """ def __init__(self): - self.abspath_str = '' + self.abspath = '' self.path = '' self.abspath_ = '' self.path_ = '' @@ -224,6 +224,61 @@ else: def _my_normcase(x): return string.upper(x) +class EntryProxy(SCons.Util.Proxy): + def __init__(self, entry): + SCons.Util.Proxy.__init__(self, entry) + self.abspath = SCons.Util.SpecialAttrWrapper(entry.abspath, + entry.name + "_abspath") + filebase, suffix = os.path.splitext(entry.name) + self.filebase = SCons.Util.SpecialAttrWrapper(filebase, + entry.name + "_filebase") + self.suffix = SCons.Util.SpecialAttrWrapper(suffix, + entry.name + "_suffix") + self.file = SCons.Util.SpecialAttrWrapper(entry.name, + entry.name + "_file") + + def __get_base_path(self): + """Return the file's directory and file name, with the + suffix stripped.""" + return SCons.Util.SpecialAttrWrapper(os.path.splitext(self.get().get_path())[0], + self.get().name + "_base") + + def __get_posix_path(self): + """Return the path with / as the path separator, regardless + of platform.""" + if os.sep == '/': + return self + else: + return SCons.Util.SpecialAttrWrapper(string.replace(self.get().get_path(), + os.sep, '/'), + self.get().name + "_posix") + + def __get_srcnode(self): + return EntryProxy(self.get().srcnode()) + + def __get_srcdir(self): + """Returns the directory containing the source node linked to this + node via BuildDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().dir) + + def __get_dir(self): + return EntryProxy(self.get().dir) + + dictSpecialAttrs = { "base" : __get_base_path, + "posix" : __get_posix_path, + "srcpath" : __get_srcnode, + "srcdir" : __get_srcdir, + "dir" : __get_dir } + + def __getattr__(self, name): + # This is how we implement the "special" attributes + # such as base, posix, srcdir, etc. + try: + return self.dictSpecialAttrs[name](self) + except KeyError: + return SCons.Util.Proxy.__getattr__(self, name) + + class Entry(SCons.Node.Node): """A generic class for file system entries. This class is for when we don't know yet whether the entry being looked up is a file @@ -251,14 +306,14 @@ class Entry(SCons.Node.Node): assert directory, "A directory must be provided" - self.abspath_str = directory.abspath_ + name + self.abspath = directory.abspath_ + name if directory.path == '.': self.path = name else: self.path = directory.path_ + name self.path_ = self.path - self.abspath_ = self.abspath_str + self.abspath_ = self.abspath self.dir = directory self.cwd = None # will hold the SConscript directory for target nodes self.duplicate = directory.duplicate @@ -293,11 +348,11 @@ class Entry(SCons.Node.Node): Since this should return the real contents from the file system, we check to see into what sort of subclass we should morph this Entry.""" - if os.path.isfile(self.abspath_str): + if os.path.isfile(self.abspath): self.__class__ = File self._morph() return File.get_contents(self) - if os.path.isdir(self.abspath_str): + if os.path.isdir(self.abspath): self.__class__ = Dir self._morph() return Dir.get_contents(self) @@ -307,7 +362,7 @@ class Entry(SCons.Node.Node): try: return self._exists except AttributeError: - self._exists = _existsp(self.abspath_str) + self._exists = _existsp(self.abspath) return self._exists def rexists(self): @@ -403,64 +458,9 @@ class Entry(SCons.Node.Node): self.sbuilder = scb return scb - def get_base_path(self): - """Return the file's directory and file name, with the - suffix stripped.""" - return os.path.splitext(self.get_path())[0] - - def get_suffix(self): - """Return the file's suffix.""" - return os.path.splitext(self.get_path())[1] - - def get_file_name(self): - """Return the file's name without the path.""" - return self.name - - def get_file_base(self): - """Return the file name with path and suffix stripped.""" - return os.path.splitext(self.name)[0] - - def get_posix_path(self): - """Return the path with / as the path separator, regardless - of platform.""" - if os.sep == '/': - return str(self) - else: - return string.replace(self.get_path(), os.sep, '/') - def get_abspath(self): """Get the absolute path of the file.""" - return self.abspath_str - - def get_srcdir(self): - """Returns the directory containing the source node linked to this - node via BuildDir(), or the directory of this node if not linked.""" - return self.srcnode().dir - - dictSpecialAttrs = { "file" : get_file_name, - "base" : get_base_path, - "filebase" : get_file_base, - "suffix" : get_suffix, - "posix" : get_posix_path, - "abspath" : get_abspath, - "srcpath" : srcnode, - "srcdir" : get_srcdir } - - def __getattr__(self, name): - # This is how we implement the "special" attributes - # such as base, suffix, basepath, etc. - # - # Note that we enclose values in a SCons.Util.Literal instance, - # so they will retain special characters during Environment variable - # substitution. - try: - attr = self.dictSpecialAttrs[name](self) - except KeyError: - raise AttributeError, '%s has no attribute: %s' % (self.__class__, name) - if SCons.Util.is_String(attr): - return SCons.Util.SpecialAttrWrapper(attr, self.name + - "_%s" % name) - return attr + return self.abspath def for_signature(self): # Return just our name. Even an absolute path would not work, @@ -468,6 +468,14 @@ class Entry(SCons.Node.Node): # paths. return self.name + def get_subst_proxy(self): + try: + return self._proxy + except AttributeError: + ret = EntryProxy(self) + self._proxy = ret + return ret + # This is for later so we can differentiate between Entry the class and Entry # the method of the FS class. _classEntry = Entry @@ -556,7 +564,7 @@ class FS: raise SCons.Errors.UserError dir = Dir(drive, ParentOfRoot(), self) dir.path = dir.path + os.sep - dir.abspath_str = dir.abspath_str + os.sep + dir.abspath = dir.abspath + os.sep self.Root[drive] = dir directory = dir path_comp = path_comp[1:] @@ -642,7 +650,7 @@ class FS: if not dir is None: self._cwd = dir if change_os_dir: - os.chdir(dir.abspath_str) + os.chdir(dir.abspath) except: self._cwd = curr raise @@ -850,7 +858,7 @@ class Dir(Entry): node) don't use signatures for currency calculation.""" self.path_ = self.path + os.sep - self.abspath_ = self.abspath_str + os.sep + self.abspath_ = self.abspath + os.sep self.repositories = [] self.srcdir = None @@ -951,9 +959,9 @@ class Dir(Entry): keys = filter(lambda k: k != '.' and k != '..', self.entries.keys()) kids = map(lambda x, s=self: s.entries[x], keys) def c(one, two): - if one.abspath_str < two.abspath_str: + if one.abspath < two.abspath: return -1 - if one.abspath_str > two.abspath_str: + if one.abspath > two.abspath: return 1 return 0 kids.sort(c) @@ -1085,11 +1093,11 @@ class File(Entry): def get_contents(self): if not self.rexists(): return '' - return open(self.rfile().abspath_str, "rb").read() + return open(self.rfile().abspath, "rb").read() def get_timestamp(self): if self.rexists(): - return os.path.getmtime(self.rfile().abspath_str) + return os.path.getmtime(self.rfile().abspath) else: return 0 @@ -1328,7 +1336,7 @@ class File(Entry): # Duplicate from source path if we are set up to do this. if self.duplicate and not self.has_builder() and not self.linked: src=self.srcnode().rfile() - if src.exists() and src.abspath_str != self.abspath_str: + if src.exists() and src.abspath != self.abspath: self._createDir() try: Unlink(self, None, None) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 209e80d..1f58264 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1524,7 +1524,7 @@ class SpecialAttrTestCase(unittest.TestCase): test=TestCmd(workdir='') fs = SCons.Node.FS.FS(test.workpath('')) - f=fs.Entry('foo/bar/baz.blat') + f=fs.Entry('foo/bar/baz.blat').get_subst_proxy() assert str(f.dir) == os.path.normpath('foo/bar'), str(f.dir) assert f.dir.is_literal() assert f.dir.for_signature() == 'bar', f.dir.for_signature() @@ -1564,11 +1564,11 @@ class SpecialAttrTestCase(unittest.TestCase): assert str(f.srcpath) == os.path.normpath('baz/bar/baz.blat'), str(f.srcpath) assert f.srcpath.is_literal() - assert isinstance(f.srcpath, SCons.Node.FS.Entry) + assert isinstance(f.srcpath.get(), SCons.Node.FS.Entry) assert str(f.srcdir) == os.path.normpath('baz/bar'), str(f.srcdir) assert f.srcdir.is_literal() - assert isinstance(f.srcdir, SCons.Node.FS.Dir) + assert isinstance(f.srcdir.get(), SCons.Node.FS.Dir) # And now, combinations!!! assert str(f.srcpath.base) == os.path.normpath('baz/bar/baz'), str(f.srcpath.base) diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index b188f81..2cf6d4b 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -949,6 +949,12 @@ class NodeTestCase(unittest.TestCase): assert n.found_includes == {}, n.found_includes assert n.implicit is None, n.implicit + def test_get_subst_proxy(self): + """Test the get_subst_proxy method.""" + n = MyNode("test") + + assert n.get_subst_proxy() == n, n.get_subst_proxy() + if __name__ == "__main__": suite = unittest.makeSuite(NodeTestCase, 'test_') diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 883d757..5231f90 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -628,7 +628,7 @@ class Node: generator is being called to generate a signature for the command line, which determines if we should rebuild or not. - Such command generators should use this method in preference + Such command generators shoud use this method in preference to str(Node) when converting a Node to a string, passing in the for_signature parameter, such that we will call Node.for_signature() or str(Node) properly, depending on whether @@ -638,6 +638,21 @@ class Node: return self.for_signature() return str(self) + def get_subst_proxy(self): + """ + This method is expected to return an object that will function + exactly like this Node, except that it implements any additional + special features that we would like to be in effect for + Environment variable substitution. The principle use is that + some Nodes would like to implement a __getattr__() method, + but putting that in the Node type itself has a tendency to kill + performance. We instead put it in a proxy and return it from + this method. It is legal for this method to return self + if no new functionality is needed for Environment substitution. + """ + return self + + def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass def do_nothing(node, parent): pass diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index b7aac31..8d81437 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -341,17 +341,26 @@ class DisplayEngine: else: self.__call__ = self.dont_print +def target_prep(target): + if target and not isinstance(target, NodeList): + if not is_List(target): + target = [target] + target = NodeList(map(lambda x: x.get_subst_proxy(), target)) + return target + +def source_prep(source): + if source and not isinstance(source, NodeList): + if not is_List(source): + source = [source] + source = NodeList(map(lambda x: x.rfile().get_subst_proxy(), source)) + return source def subst_dict(target, source, env): - """Create a dictionary for substitution of construction - variables. + """Create a dictionary for substitution of special + construction variables. This translates the following special arguments: - env - the construction environment itself, - the values of which (CC, CCFLAGS, etc.) - are copied straight into the dictionary - target - the target (object or array of objects), used to generate the TARGET and TARGETS construction variables @@ -359,20 +368,20 @@ def subst_dict(target, source, env): source - the source (object or array of objects), used to generate the SOURCES and SOURCE construction variables - """ - - dict = env.Dictionary().copy() - if not is_List(target): - target = [target] + env - the construction Environment used for this + build, which is made available as the __env__ + construction variable + """ + dict = { '__env__' : env } - dict['TARGETS'] = NodeList(target) + target = target_prep(target) + dict['TARGETS'] = target if dict['TARGETS']: dict['TARGET'] = dict['TARGETS'][0] - if not is_List(source): - source = [source] - dict['SOURCES'] = NodeList(map(lambda x: x.rfile(), source)) + source = source_prep(source) + dict['SOURCES'] = source if dict['SOURCES']: dict['SOURCE'] = dict['SOURCES'][0] @@ -406,8 +415,7 @@ def _canonicalize(obj): _regex_remove = [ None, _rm, _remove ] _strconv = [ to_String, to_String, _canonicalize ] -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, - source=None): +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None): """ This function serves the same purpose as scons_subst(), except this function returns the interpolated list as a list of lines, where @@ -434,18 +442,13 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, remove = _regex_remove[mode] strconv = _strconv[mode] - - if target != None: - dict = subst_dict(target, source, env) - else: - dict = env.Dictionary() def repl(m, target=target, source=source, env=env, - local_vars = dict, - global_vars = { "__env__" : env }, + local_vars = subst_dict(target, source, env), + global_vars = env.Dictionary(), strconv=strconv, sig=(mode != SUBST_CMD)): key = m.group(1) @@ -492,8 +495,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, return map(lambda x: map(CmdStringHolder, filter(lambda y:y, string.split(x, '\0\1'))), listLines) -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, - source=None): +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None): """Recursively interpolates dictionary variables into the specified string, returning the expanded result. Variables are specified by a $ prefix in the string and @@ -506,11 +508,6 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, # This function needs to be fast, so don't call scons_subst_list - if target != None: - dict = subst_dict(target, source, env) - else: - dict = env.Dictionary() - remove = _regex_remove[mode] strconv = _strconv[mode] @@ -518,8 +515,8 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, target=target, source=source, env=env, - local_vars = dict, - global_vars = { '__env__' : env }, + local_vars = subst_dict(target, source, env), + global_vars = env.Dictionary(), strconv=strconv, sig=(mode != SUBST_CMD)): key = m.group(1) @@ -530,7 +527,8 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, except NameError: return '\0\5' if callable(e): - e = e(target=target, source=source, env=env, for_signature=sig) + e = e(target=target, source=source, env=env, + for_signature = sig) def conv(arg, strconv=strconv): literal = 0 @@ -691,6 +689,9 @@ class Proxy: def __getattr__(self, name): return getattr(self.__subject, name) + def get(self): + return self.__subject + # attempt to load the windows registry module: can_read_reg = 0 try: diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index e0543d9..a48b302 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -95,6 +95,9 @@ class UtilTestCase(unittest.TestCase): def rfile(self): return self + def get_subst_proxy(self): + return self + foo = 1 target = [ N("./foo/bar.exe"), @@ -241,6 +244,8 @@ class UtilTestCase(unittest.TestCase): return 1 def rfile(self): return self + def get_subst_proxy(self): + return self loc = {} target = [ Node("./foo/bar.exe"), @@ -589,6 +594,7 @@ class UtilTestCase(unittest.TestCase): s.baz = 6 assert p.baz == 5, p.baz + assert p.get() == s, p.get() def test_Literal(self): """Test the Literal() function.""" @@ -710,9 +716,9 @@ class UtilTestCase(unittest.TestCase): def test_subst_dict(self): """Test substituting dictionary values in an Action """ - d = subst_dict([], [], DummyEnv({'a' : 'A', 'b' : 'B'})) - assert d['a'] == 'A', d - assert d['b'] == 'B', d + env = DummyEnv({'a' : 'A', 'b' : 'B'}) + d = subst_dict([], [], env) + assert d['__env__'] is env, d['__env__'] class SimpleNode: def __init__(self, data): @@ -723,6 +729,8 @@ class UtilTestCase(unittest.TestCase): return self def is_literal(self): return 1 + def get_subst_proxy(self): + return self d = subst_dict(target = SimpleNode('t'), source = SimpleNode('s'), env=DummyEnv()) assert str(d['TARGETS'][0]) == 't', d['TARGETS'] @@ -749,6 +757,8 @@ class UtilTestCase(unittest.TestCase): return self.name def rfile(self): return self.__class__('rstr-' + self.name) + def get_subst_proxy(self): + return self d = subst_dict(target = [N('t3'), SimpleNode('t4')], source = [SimpleNode('s3'), N('s4')], |