diff options
author | Steven Knight <knight@baldmt.com> | 2002-09-27 23:03:51 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2002-09-27 23:03:51 (GMT) |
commit | f6141b3f503c8f78fca191bfd59d9785cd81bf10 (patch) | |
tree | 5308bfa21af4adb91d1126edef0648e5fdedb331 /src/engine | |
parent | 1523e6f372549807f31962bfbb9d429ead2db9d2 (diff) | |
download | SCons-f6141b3f503c8f78fca191bfd59d9785cd81bf10.zip SCons-f6141b3f503c8f78fca191bfd59d9785cd81bf10.tar.gz SCons-f6141b3f503c8f78fca191bfd59d9785cd81bf10.tar.bz2 |
Support overriding construction variables in builder call. (Anthony Roach)
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/Action.py | 182 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 59 | ||||
-rw-r--r-- | src/engine/SCons/Builder.py | 70 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 106 | ||||
-rw-r--r-- | src/engine/SCons/Environment.py | 66 | ||||
-rw-r--r-- | src/engine/SCons/EnvironmentTests.py | 8 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 5 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 30 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 23 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 9 |
10 files changed, 296 insertions, 262 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 7aecf00..fcd0ec9 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -263,7 +263,7 @@ class ActionBase: def show(self, string): print string - def subst_dict(self, **kw): + def subst_dict(self, target, source, env): """Create a dictionary for substitution of construction variables. @@ -278,52 +278,31 @@ class ActionBase: construction variables source - the source (object or array of objects), - used to generate the SOURCES and SOURCE + used to generate the SOURCES and SOURCE construction variables - - Any other keyword arguments are copied into the - dictionary.""" + """ dict = {} - if kw.has_key('env'): - dict.update(kw['env']) - del kw['env'] - try: - cwd = kw['dir'] - except KeyError: - cwd = None - else: - del kw['dir'] + for k,v in env.items(): dict[k] = v - if kw.has_key('target'): - t = kw['target'] - del kw['target'] - if not SCons.Util.is_List(t): - t = [t] - try: - cwd = t[0].cwd - except (IndexError, AttributeError): - pass - dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t))) - if dict['TARGETS']: - dict['TARGET'] = dict['TARGETS'][0] + if not SCons.Util.is_List(target): + target = [target] - if kw.has_key('source'): - def rstr(x): - try: - return x.rstr() - except AttributeError: - return str(x) - s = kw['source'] - del kw['source'] - if not SCons.Util.is_List(s): - s = [s] - dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, s))) - if dict['SOURCES']: - dict['SOURCE'] = dict['SOURCES'][0] + dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target))) + if dict['TARGETS']: + dict['TARGET'] = dict['TARGETS'][0] - dict.update(kw) + def rstr(x): + try: + return x.rstr() + except AttributeError: + return str(x) + if not SCons.Util.is_List(source): + source = [source] + dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source))) + if dict['SOURCES']: + dict['SOURCE'] = dict['SOURCES'][0] return dict @@ -340,44 +319,15 @@ def _string_from_cmd_list(cmd_list): _rm = re.compile(r'\$[()]') _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)') -class EnvDictProxy(UserDict.UserDict): - """This is a dictionary-like class that contains the - Environment dictionary we pass to FunctionActions - and CommandGeneratorActions. - - In addition to providing - normal dictionary-like access to the variables in the - Environment, it also exposes the functions subst() - and subst_list(), allowing users to easily do variable - interpolation when writing their FunctionActions - and CommandGeneratorActions.""" - - def __init__(self, env): - UserDict.UserDict.__init__(self, env) - - def subst(self, string, raw=0): - if raw: - regex_remove = None - else: - regex_remove = _rm - return SCons.Util.scons_subst(string, self.data, {}, regex_remove) - - def subst_list(self, string, raw=0): - if raw: - regex_remove = None - else: - regex_remove = _rm - return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove) - class CommandAction(ActionBase): """Class for command-execution actions.""" def __init__(self, cmd): import SCons.Util - + self.cmd_list = map(SCons.Util.to_String, cmd) - def execute(self, **kw): - dict = apply(self.subst_dict, (), kw) + def execute(self, target, source, env): + dict = self.subst_dict(target, source, env) import SCons.Util cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm) for cmd_line in cmd_list: @@ -386,7 +336,7 @@ class CommandAction(ActionBase): self.show(_string_from_cmd_list(cmd_line)) if execute_actions: try: - ENV = kw['env']['ENV'] + ENV = dict['ENV'] except KeyError: global default_ENV if not default_ENV: @@ -398,7 +348,7 @@ class CommandAction(ActionBase): return ret return 0 - def _sig_dict(self, kw): + def _sig_dict(self, target, source, env): """Supply a dictionary for use in computing signatures. For signature purposes, it doesn't matter what targets or @@ -406,62 +356,52 @@ class CommandAction(ActionBase): so the signature stays the same. We supply an array of two of each to allow for distinction between TARGET and TARGETS. """ - kw['target'] = ['__t1__', '__t2__'] - kw['source'] = ['__s1__', '__s2__'] - return apply(self.subst_dict, (), kw) + return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env) - def get_raw_contents(self, **kw): + def get_raw_contents(self, target, source, env): """Return the complete contents of this action's command line. """ return SCons.Util.scons_subst(string.join(self.cmd_list), - self._sig_dict(kw), {}) + self._sig_dict(target, source, env), {}) - def get_contents(self, **kw): + def get_contents(self, target, source, env): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ return SCons.Util.scons_subst(string.join(self.cmd_list), - self._sig_dict(kw), {}, _remove) + self._sig_dict(target, source, env), {}, _remove) class CommandGeneratorAction(ActionBase): """Class for command-generator actions.""" def __init__(self, generator): self.generator = generator - def __generate(self, kw, for_signature): + def __generate(self, target, source, env, for_signature): import SCons.Util - # Wrap the environment dictionary in an EnvDictProxy - # object to make variable interpolation easier for the - # client. - args = copy.copy(kw) - args['for_signature'] = for_signature - if args.has_key("env") and not isinstance(args["env"], EnvDictProxy): - args["env"] = EnvDictProxy(args["env"]) - # ensure that target is a list, to make it easier to write # generator functions: - if args.has_key("target") and not SCons.Util.is_List(args["target"]): - args["target"] = [args["target"]] + if not SCons.Util.is_List(target): + target = [target] - ret = apply(self.generator, (), args) + ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) gen_cmd = Action(ret) if not gen_cmd: raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) return gen_cmd - def execute(self, **kw): - return apply(self.__generate(kw, 0).execute, (), kw) + def execute(self, target, source, env): + return self.__generate(target, source, env, 0).execute(target, source, env) - def get_contents(self, **kw): + def get_contents(self, target, source, env): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ - return apply(self.__generate(kw, 1).get_contents, (), kw) + return self.__generate(target, source, env, 1).get_contents(target, source, env) class LazyCmdGenerator: """This is a simple callable class that acts as a command generator. @@ -471,7 +411,7 @@ class LazyCmdGenerator: def __init__(self, var): self.var = SCons.Util.to_String(var) - def __call__(self, env, **kw): + def __call__(self, target, source, env, for_signature): if env.has_key(self.var): return env[self.var] else: @@ -483,27 +423,25 @@ class FunctionAction(ActionBase): def __init__(self, function): self.function = function - def execute(self, **kw): + def execute(self, target, source, env): # if print_actions: # XXX: WHAT SHOULD WE PRINT HERE? if execute_actions: - if kw.has_key('target') and not \ - SCons.Util.is_List(kw['target']): - kw['target'] = [ kw['target'] ] - if kw.has_key('source'): - def rfile(n): - try: - return n.rfile() - except AttributeError: - return n - if not SCons.Util.is_List(kw['source']): - kw['source'] = [ kw['source'] ] - kw['source'] = map(rfile, kw['source']) - if kw.has_key("env") and not isinstance(kw["env"], EnvDictProxy): - kw["env"] = EnvDictProxy(kw["env"]) - return apply(self.function, (), kw) - - def get_contents(self, **kw): + if not SCons.Util.is_List(target): + target = [target] + + def rfile(n): + try: + return n.rfile() + except AttributeError: + return n + if not SCons.Util.is_List(source): + source = [source] + source = map(rfile, source) + + return self.function(target=target, source=source, env=env) + + def get_contents(self, target, source, env): """Return the signature contents of this callable action. By providing direct access to the code object of the @@ -524,17 +462,21 @@ class ListAction(ActionBase): def __init__(self, list): self.list = map(lambda x: Action(x), list) - def execute(self, **kw): + def execute(self, target, source, env): for l in self.list: - r = apply(l.execute, (), kw) + r = l.execute(target, source, env) if r: return r return 0 - def get_contents(self, **kw): + def get_contents(self, target, source, env): """Return the signature contents of this action list. Simple concatenation of the signatures of the elements. """ - return reduce(lambda x, y, kw=kw: x + str(apply(y.get_contents, (), kw)), self.list, "") + ret = "" + for a in self.list: + ret = ret + a.get_contents(target, source, env) + return ret + diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 16bcd80..6ce52d5 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -37,6 +37,11 @@ import unittest import SCons.Action import TestCmd +import UserDict + +import SCons.Environment +Environment = SCons.Environment.EnvProxy + class ActionTestCase(unittest.TestCase): def runTest(self): @@ -93,18 +98,18 @@ class ActionBaseTestCase(unittest.TestCase): """ a = SCons.Action.Action("x") - d = a.subst_dict(env = {'a' : 'A', 'b' : 'B'}) + d = a.subst_dict([],[],Environment({'a' : 'A', 'b' : 'B'})) assert d['a'] == 'A', d assert d['b'] == 'B', d - d = a.subst_dict(target = 't', source = 's') + d = a.subst_dict(target = 't', source = 's',env=Environment({})) assert str(d['TARGETS']) == 't', d['TARGETS'] assert str(d['TARGET']) == 't', d['TARGET'] assert str(d['SOURCES']) == 's', d['SOURCES'] assert str(d['SOURCE']) == 's', d['SOURCE'] - d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2']) + d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2'], env=Environment({})) TARGETS = map(lambda x: str(x), d['TARGETS']) TARGETS.sort() assert TARGETS == ['t1', 't2'], d['TARGETS'] @@ -122,7 +127,7 @@ class ActionBaseTestCase(unittest.TestCase): def rstr(self): return 'rstr-' + self.name - d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')]) + d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')], env=Environment({})) TARGETS = map(lambda x: str(x), d['TARGETS']) TARGETS.sort() assert TARGETS == ['t3', 't4'], d['TARGETS'] @@ -157,7 +162,7 @@ class CommandActionTestCase(unittest.TestCase): SCons.Action.SetCommandHandler(func) assert SCons.Action.spawn is func a = SCons.Action.CommandAction(["xyzzy"]) - a.execute() + a.execute([],[],Environment({})) assert t.executed == 1 def test_get_raw_contents(self): @@ -175,7 +180,7 @@ class CommandActionTestCase(unittest.TestCase): a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar", "$)", "|"]) c = a.get_contents(target=[], source=[], - foo = 'FFF', bar = 'BBB') + env=Environment({'foo':'FFF', 'bar':'BBB'})) assert c == "| |", c class CommandGeneratorActionTestCase(unittest.TestCase): @@ -192,7 +197,8 @@ class CommandGeneratorActionTestCase(unittest.TestCase): """Test executing a command generator Action """ - def f(dummy, env, for_signature, self=self): + def f(target, source, env, for_signature, self=self): + dummy = env['dummy'] self.dummy = dummy assert env.subst("$FOO $( bar $) baz") == 'foo baz\nbar ack bar baz', env.subst("$FOO $( bar $) baz") assert env.subst("$FOO $( bar $) baz", raw=1) == 'foo baz\nbar ack $( bar $) baz', env.subst("$FOO $( bar $) baz", raw=1) @@ -202,7 +208,8 @@ class CommandGeneratorActionTestCase(unittest.TestCase): raw=1) == [ [ 'foo', 'baz' ], [ 'bar', 'ack', '$(', 'bar', '$)', 'baz' ] ], env.subst_list("$FOO $( bar $) baz", raw=1) return "$FOO" - def func_action(env, dummy, self=self): + def func_action(target, source,env, self=self): + dummy=env['dummy'] assert env.subst('$foo $( bar $)') == 'bar bar', env.subst('$foo $( bar $)') assert env.subst('$foo $( bar $)', raw=1) == 'bar $( bar $)', env.subst('$foo $( bar $)', raw=1) @@ -210,7 +217,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): assert env.subst_list([ '$foo', '$(', 'bar', '$)' ], raw=1) == [[ 'bar', '$(', 'bar', '$)' ]], env.subst_list([ '$foo', '$(', 'bar', '$)' ], raw=1) self.dummy=dummy - def f2(dummy, env, for_signature, f=func_action): + def f2(target, source, env, for_signature, f=func_action): return f def ch(cmd, args, env, self=self): self.cmd.append(cmd) @@ -223,7 +230,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): self.args = [] try: SCons.Action.SetCommandHandler(ch) - a.execute(dummy=1, env={ 'FOO' : 'foo baz\nbar ack' }) + a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack' , 'dummy':1})) finally: SCons.Action.SetCommandHandler(old_hdl) assert self.dummy == 1 @@ -232,20 +239,22 @@ class CommandGeneratorActionTestCase(unittest.TestCase): b=SCons.Action.CommandGeneratorAction(f2) self.dummy = 0 - b.execute(dummy=2, env={ 'foo' : 'bar' }) + b.execute(target=[], source=[], env=Environment({ 'foo' : 'bar','dummy':2 })) assert self.dummy==2, self.dummy del self.dummy def test_get_contents(self): """Test fetching the contents of a command generator Action """ - def f(target, source, foo, bar, for_signature): + def f(target, source, env, for_signature): + foo = env['foo'] + bar = env['bar'] assert for_signature, for_signature return [["guux", foo, "$(", "ignore", "$)", bar]] a = SCons.Action.CommandGeneratorAction(f) c = a.get_contents(target=[], source=[], - foo = 'FFF', bar = 'BBB') + env=Environment({'foo':'FFF', 'bar' : 'BBB'})) assert c == "guux FFF BBB", c @@ -263,7 +272,8 @@ class FunctionActionTestCase(unittest.TestCase): """Test executing a function Action """ self.inc = 0 - def f(s, target, source, env): + def f(target, source, env): + s = env['s'] s.inc = s.inc + 1 s.target = target s.source=source @@ -272,7 +282,7 @@ class FunctionActionTestCase(unittest.TestCase): env.subst_list("foo$BAR") return 0 a = SCons.Action.FunctionAction(f) - a.execute(s = self, target=1, source=2, env={'BAR':'foo bar'}) + a.execute(target=1, source=2, env=Environment({'BAR':'foo bar','s':self})) assert self.inc == 1, self.inc assert self.source == [2], self.source assert self.target == [1], self.target @@ -281,7 +291,7 @@ class FunctionActionTestCase(unittest.TestCase): """Test fetching the contents of a function Action """ a = SCons.Action.FunctionAction(Func) - c = a.get_contents(target=[], source=[]) + c = a.get_contents(target=[], source=[], env=Environment({})) assert c == "\177\036\000\177\037\000d\000\000S", repr(c) class ListActionTestCase(unittest.TestCase): @@ -301,23 +311,25 @@ class ListActionTestCase(unittest.TestCase): """Test executing a list of subsidiary Actions """ self.inc = 0 - def f(s): + def f(target,source,env): + s = env['s'] s.inc = s.inc + 1 a = SCons.Action.ListAction([f, f, f]) - a.execute(s = self) + a.execute([],[],Environment({'s':self})) assert self.inc == 3, self.inc def test_get_contents(self): """Test fetching the contents of a list of subsidiary Actions """ self.foo=0 - def gen(target, source, s, for_signature): + def gen(target, source, env, for_signature): + s = env['s'] s.foo=1 return "y" a = SCons.Action.ListAction(["x", SCons.Action.CommandGenerator(gen), "z"]) - c = a.get_contents(target=[], source=[], s=self) + c = a.get_contents(target=[], source=[], env=Environment({'s':self})) assert self.foo==1, self.foo assert c == "xyz", c @@ -339,11 +351,12 @@ class LazyActionTestCase(unittest.TestCase): def test_execute(self): """Test executing a lazy-evalueation Action """ - def f(s, env): + def f(target, source, env): + s = env['s'] s.test=1 return 0 a = SCons.Action.Action('$BAR') - a.execute(s = self, env={'BAR':f}) + a.execute([],[], env=Environment({'BAR':f,'s':self})) assert self.test == 1, self.test def test_get_contents(self): @@ -351,7 +364,7 @@ class LazyActionTestCase(unittest.TestCase): """ a = SCons.Action.Action("${FOO}") c = a.get_contents(target=[], source=[], - env={'FOO':[["This", "is", "$(", "a", "$)", "test"]]}) + env=Environment({'FOO':[["This", "is", "$(", "a", "$)", "test"]]})) assert c == "This is test", c diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index fe6283b..02f9e2d 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -79,7 +79,7 @@ class DictCmdGenerator: """ self.action_dict[suffix] = action - def __call__(self, source, target, env, **kw): + def __call__(self, target, source, env, for_signature): ext = None for src in map(str, source): my_ext = SCons.Util.splitext(src)[1] @@ -124,7 +124,7 @@ def Builder(**kw): if not var: raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % kw['emitter'] kw['emitter'] = EmitterProxy(var) - + if kw.has_key('src_builder'): ret = apply(MultiStepBuilder, (), kw) else: @@ -135,7 +135,7 @@ def Builder(**kw): return ret -def _init_nodes(builder, env, args, tlist, slist): +def _init_nodes(builder, env, overrides, tlist, slist): """Initialize lists of target and source nodes with all of the proper Builder information. """ @@ -150,10 +150,10 @@ def _init_nodes(builder, env, args, tlist, slist): if t.side_effect: raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) if t.builder is not None: - if t.env != env: + if t.env != env: raise UserError, "Two different environments were specified for the same target: %s"%str(t) - elif t.build_args != args: - raise UserError, "Two different sets of build arguments were specified for the same target: %s"%str(t) + elif t.overrides != overrides: + raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t) elif builder.scanner and builder.scanner != t.target_scanner: raise UserError, "Two different scanners were specified for the same target: %s"%str(t) @@ -166,14 +166,14 @@ def _init_nodes(builder, env, args, tlist, slist): elif t.sources != slist: raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) - t.build_args = args + t.overrides = overrides t.cwd = SCons.Node.FS.default_fs.getcwd() t.builder_set(builder) t.env_set(env) t.add_source(slist) if builder.scanner: t.target_scanner = builder.scanner - + def _adjust_suffix(suff): if suff and not suff[0] in [ '.', '$' ]: return '.' + suff @@ -189,7 +189,7 @@ class EmitterProxy: def __init__(self, var): self.var = SCons.Util.to_String(var) - def __call__(self, target, source, env, **kw): + def __call__(self, target, source, env): emitter = self.var # Recursively substitute the variable. @@ -200,11 +200,8 @@ class EmitterProxy: emitter = env[emitter] if not callable(emitter): return (target, source) - args = { 'target':target, - 'source':source, - 'env':env } - args.update(kw) - return apply(emitter, (), args) + + return emitter(target, source, env) def __cmp__(self, other): return cmp(self.var, other.var) @@ -258,7 +255,7 @@ class BuilderBase: def __cmp__(self, other): return cmp(self.__dict__, other.__dict__) - def _create_nodes(self, env, args, target = None, source = None): + def _create_nodes(self, env, overrides, target = None, source = None): """Create and return lists of target and source nodes. """ def adjustixes(files, pre, suf): @@ -280,6 +277,8 @@ class BuilderBase: ret.append(f) return ret + env = env.Override(overrides) + pre = self.get_prefix(env) suf = self.get_suffix(env) src_suf = self.get_src_suffix(env) @@ -294,49 +293,45 @@ class BuilderBase: if self.emitter: # pass the targets and sources to the emitter as strings - # rather than nodes since str(node) doesn't work + # rather than nodes since str(node) doesn't work # properly from any directory other than the top directory, # and emitters are called "in" the SConscript directory: - emit_args = { 'target' : target, - 'source' : source, - 'env' : env } - emit_args.update(args) - target, source = apply(self.emitter, (), emit_args) + target, source = self.emitter(target=target, source=source, env=env) slist = SCons.Node.arg2nodes(source, self.source_factory) tlist = SCons.Node.arg2nodes(target, self.target_factory) return tlist, slist - def __call__(self, env, target = None, source = _null, **kw): + def __call__(self, env, target = None, source = _null, **overrides): if source is _null: source = target target = None - tlist, slist = self._create_nodes(env, kw, target, source) + tlist, slist = self._create_nodes(env, overrides, target, source) if len(tlist) == 1: - _init_nodes(self, env, kw, tlist, slist) + _init_nodes(self, env, overrides, tlist, slist) tlist = tlist[0] else: - _init_nodes(ListBuilder(self, env, tlist), env, kw, tlist, slist) + _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist) return tlist - def execute(self, **kw): + def execute(self, target, source, env): """Execute a builder's action to create an output object. """ - return apply(self.action.execute, (), kw) + return self.action.execute(target, source, env) - def get_raw_contents(self, **kw): + def get_raw_contents(self, target, source, env): """Fetch the "contents" of the builder's action. """ - return apply(self.action.get_raw_contents, (), kw) + return self.action.get_raw_contents(target, source, env) - def get_contents(self, **kw): + def get_contents(self, target, source, env): """Fetch the "contents" of the builder's action (for signature calculation). """ - return apply(self.action.get_contents, (), kw) + return self.action.get_contents(target, source, env) def src_suffixes(self, env): return map(lambda x, e=env: e.subst(_adjust_suffix(x)), @@ -383,17 +378,17 @@ class ListBuilder(SCons.Util.Proxy): self.multi = builder.multi self.name = "ListBuilder(%s)"%builder.name - def execute(self, **kw): + def execute(self, target, source, env): if hasattr(self, 'status'): return self.status for t in self.tlist: # unlink all targets and make all directories # before building anything t.prepare() - kw['target'] = self.tlist - self.status = apply(self.builder.execute, (), kw) + target = self.tlist + self.status = self.builder.execute(target, source, env) for t in self.tlist: - if not t is kw['target']: + if not t is target: t.build() return self.status @@ -443,7 +438,7 @@ class MultiStepBuilder(BuilderBase): if not SCons.Util.is_List(src_builder): src_builder = [ src_builder ] self.src_builder = src_builder - self.sdict = {} + self.sdict = {} self.cached_src_suffixes = {} # source suffixes keyed on id(env) def __call__(self, env, target = None, source = None, **kw): @@ -463,8 +458,9 @@ class MultiStepBuilder(BuilderBase): continue for suf in bld.src_suffixes(env): sdict[suf] = bld - + src_suffixes = self.src_suffixes(env) + for snode in slist: path, ext = SCons.Util.splitext(snode.abspath) if sdict.has_key(ext): diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index e862b05..1917405 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -94,6 +94,16 @@ class Environment: return self.d[item] def has_key(self, item): return self.d.has_key(item) + def keys(self): + return self.d.keys() + def get(self, key, value): + return self.d.get(key, value) + def Override(self, overrides): + env = apply(Environment, (), self.d) + env.d.update(overrides) + return env + def items(self): + return self.d.items() env = Environment() @@ -222,7 +232,7 @@ class BuilderTestCase(unittest.TestCase): cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile) builder = MyBuilder(action = cmd1, name = "cmd1") - r = builder.execute() + r = builder.execute([],[],Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'xyzzy'\n", c @@ -230,7 +240,7 @@ class BuilderTestCase(unittest.TestCase): cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile) builder = MyBuilder(action = cmd2, name = "cmd2") - r = builder.execute(target = 'foo') + r = builder.execute('foo', [], Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'foo'\n", c @@ -238,7 +248,7 @@ class BuilderTestCase(unittest.TestCase): cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile) builder = MyBuilder(action = cmd3, name = "cmd3") - r = builder.execute(target = ['aaa', 'bbb']) + r = builder.execute(['aaa', 'bbb'], [], Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'aaa' 'bbb'\n", c @@ -246,7 +256,7 @@ class BuilderTestCase(unittest.TestCase): cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile) builder = MyBuilder(action = cmd4, name = "cmd4") - r = builder.execute(source = ['one', 'two']) + r = builder.execute([], ['one', 'two'], Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'one' 'two'\n", c @@ -254,7 +264,7 @@ class BuilderTestCase(unittest.TestCase): cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile) builder = MyBuilder(action = cmd4, name = "cmd4") - r = builder.execute(source = ['three', 'four', 'five']) + r = builder.execute([], source = ['three', 'four', 'five'], env=Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'three' 'four'\n", c @@ -262,7 +272,7 @@ class BuilderTestCase(unittest.TestCase): cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile) builder = MyBuilder(action = cmd5, name = "cmd5") - r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}}) + r = builder.execute(target = 'out5', source = [], env = Environment(ENV={'XYZZY' : 'xyzzy'})) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c @@ -277,7 +287,8 @@ class BuilderTestCase(unittest.TestCase): builder = MyBuilder(action = cmd6, name = "cmd6") r = builder.execute(target = [Obj('111'), Obj('222')], - source = [Obj('333'), Obj('444'), Obj('555')]) + source = [Obj('333'), Obj('444'), Obj('555')], + env = Environment()) assert r == 0 c = test.read(outfile, 'r') assert c == "act.py: '222' '111' '333' '444'\n", c @@ -297,24 +308,22 @@ class BuilderTestCase(unittest.TestCase): for action in builder.action.list: action.show = my_show - r = builder.execute() + r = builder.execute([],[],Environment()) assert r == 0 assert show_string == expect7, show_string global count count = 0 - def function1(**kw): + def function1(target, source, env): global count count = count + 1 - if not type(kw['target']) is type([]): - kw['target'] = [ kw['target'] ] - for t in kw['target']: + for t in target: open(t, 'w').write("function1\n") return 1 builder = MyBuilder(action = function1, name = "function1") try: - r = builder.execute(target = [outfile, outfile2]) + r = builder.execute(target = [outfile, outfile2], source=[], env=Environment()) except SCons.Errors.BuildError: pass assert r == 1 @@ -325,43 +334,43 @@ class BuilderTestCase(unittest.TestCase): assert c == "function1\n", c class class1a: - def __init__(self, **kw): - open(kw['out'], 'w').write("class1a\n") + def __init__(self, target, source, env): + open(env['out'], 'w').write("class1a\n") builder = MyBuilder(action = class1a, name = "class1a") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r.__class__ == class1a c = test.read(outfile, 'r') assert c == "class1a\n", c class class1b: - def __call__(self, **kw): - open(kw['out'], 'w').write("class1b\n") + def __call__(self, target, source, env): + open(env['out'], 'w').write("class1b\n") return 2 builder = MyBuilder(action = class1b(), name = "class1b") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r == 2 c = test.read(outfile, 'r') assert c == "class1b\n", c cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile) - def function2(**kw): - open(kw['out'], 'a').write("function2\n") + def function2(target, source, env): + open(env['out'], 'a').write("function2\n") return 0 class class2a: - def __call__(self, **kw): - open(kw['out'], 'a').write("class2a\n") + def __call__(self, target, source, env): + open(env['out'], 'a').write("class2a\n") return 0 class class2b: - def __init__(self, **kw): - open(kw['out'], 'a').write("class2b\n") + def __init__(self, target, source, env): + open(env['out'], 'a').write("class2b\n") builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r.__class__ == class2b c = test.read(outfile, 'r') assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c @@ -377,18 +386,18 @@ class BuilderTestCase(unittest.TestCase): # Test that a nonexistent command returns 127 builder = MyBuilder(action = python + "_XyZzY_", name="badcmd") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r == expect_nonexistent, "r == %d" % r # Test that trying to execute a directory returns 126 dir, tail = os.path.split(python) builder = MyBuilder(action = dir, name = "dir") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r == expect_nonexecutable, "r == %d" % r # Test that trying to execute a non-executable file returns 126 builder = MyBuilder(action = outfile, name = "badfile") - r = builder.execute(out = outfile) + r = builder.execute([],[],Environment(out = outfile)) assert r == expect_nonexecutable, "r == %d" % r def test_get_contents(self): @@ -396,15 +405,15 @@ class BuilderTestCase(unittest.TestCase): """ b1 = SCons.Builder.Builder(name = "b1", action = "foo") - contents = b1.get_contents() + contents = b1.get_contents([],[],Environment()) assert contents == "foo", contents b2 = SCons.Builder.Builder(name = "b2", action = Func) - contents = b2.get_contents() + contents = b2.get_contents([],[],Environment()) assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents) b3 = SCons.Builder.Builder(name = "b3", action = SCons.Action.ListAction(["foo", Func, "bar"])) - contents = b3.get_contents() + contents = b3.get_contents([],[],Environment()) assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents) def test_node_factory(self): @@ -517,27 +526,27 @@ class BuilderTestCase(unittest.TestCase): """Testing ListBuilder class.""" global count count = 0 - def function2(tlist = [outfile, outfile2], **kw): + def function2(target, source, env, tlist = [outfile, outfile2], **kw): global count count = count + 1 - for t in kw['target']: + for t in target: open(str(t), 'w').write("function2\n") for t in tlist: - if not t in map(str, kw['target']): + if not t in map(str, target): open(t, 'w').write("function2\n") return 1 builder = SCons.Builder.Builder(action = function2, name = "function2") tgts = builder(env, target = [outfile, outfile2], source = 'foo') try: - r = tgts[0].builder.execute(target = tgts) + r = tgts[0].builder.execute(tgts, 'foo', env) except SCons.Errors.BuildError: pass c = test.read(outfile, 'r') assert c == "function2\n", c c = test.read(outfile2, 'r') assert c == "function2\n", c - r = tgts[1].builder.execute(target = tgts[1]) + r = tgts[1].builder.execute(tgts[1], 'foo', env) assert r == 1, r assert count == 1, count @@ -545,20 +554,20 @@ class BuilderTestCase(unittest.TestCase): sub2_out = test.workpath('sub2', 'out') count = 0 - def function3(tlist = [sub1_out, sub2_out], **kw): + def function3(target, source, env, tlist = [sub1_out, sub2_out]): global count count = count + 1 - for t in kw['target']: + for t in target: open(str(t), 'w').write("function3\n") for t in tlist: - if not t in map(str, kw['target']): + if not t in map(str, target): open(t, 'w').write("function3\n") return 1 builder = SCons.Builder.Builder(action = function3, name = "function3") tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo') try: - r = tgts[0].builder.execute(target = tgts) + r = tgts[0].builder.execute(tgts, 'foo', env) except: pass assert r == 1, r @@ -717,19 +726,24 @@ class BuilderTestCase(unittest.TestCase): def test_Builder_Args(self): """Testing passing extra agrs to a builder.""" - def buildFunc(target, source, env, foo, bar, s=self): - s.foo=foo - s.bar=bar + def buildFunc(target, source, env, s=self): + s.foo=env['foo'] + s.bar=env['bar'] + assert env['CC'] == 'mycc' + + env=Environment(CC='cc') builder = SCons.Builder.Builder(name="builder", action=buildFunc) - tgt = builder(env, target='foo', source='bar', foo=1, bar=2) + tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc') tgt.build() assert self.foo == 1, self.foo assert self.bar == 2, self.bar def test_emitter(self): """Test emitter functions.""" - def emit(target, source, env, foo=0, bar=0): + def emit(target, source, env): + foo = env.get('foo', 0) + bar = env.get('bar', 0) if foo: target.append("bar%d"%foo) if bar: diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index a512d93..24315a1 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -50,12 +50,12 @@ from UserDict import UserDict import SCons.Platform import SCons.Tool -def installFunc(env, target, source): +def installFunc(target, source, env): try: map(lambda t: os.unlink(str(t)), target) except OSError: pass - + try: SCons.Node.FS.file_link(str(source[0]), str(target[0])) print 'Install file: "%s" as "%s"' % \ @@ -74,8 +74,8 @@ InstallBuilder = SCons.Builder.Builder(name='Install', action=installFunc) def our_deepcopy(x): - """deepcopy lists and dictionaries, and just copy the reference - for everything else.""" + """deepcopy lists and dictionaries, and just copy the reference + for everything else.""" if SCons.Util.is_Dict(x): copy = {} for key in x.keys(): @@ -109,6 +109,44 @@ class BuilderDict(UserDict): UserDict.__delitem__(self, item) self.env.Replace() +_rm = re.compile(r'\$[()]') + +class EnvProxy(UserDict): + """This is a dictionary-like class that is returned + by Environment.Override(). + + In addition to providing + normal dictionary-like access to the variables in the + Environment, it also exposes the functions subst() + and subst_list(), allowing users to easily do variable + interpolation when writing their FunctionActions + and CommandGeneratorActions.""" + + def __init__(self, env): + UserDict.__init__(self, env) + + def subst(self, string, raw=0): + if raw: + regex_remove = None + else: + regex_remove = _rm + return SCons.Util.scons_subst(string, self.data, {}, regex_remove) + + def subst_list(self, string, raw=0): + if raw: + regex_remove = None + else: + regex_remove = _rm + return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove) + + def Override(self, overrides): + if overrides: + proxy = EnvProxy(self) + proxy.update(overrides) + return proxy + else: + return self + class Environment: """Base class for construction Environments. These are the primary objects used to communicate dependency and @@ -478,6 +516,26 @@ class Environment: if path: return prog return None + def Override(self, overrides): + """ + Produce a modified psuedo-environment whose variables + are overriden by the overrides dictionaries. + + overrides - a dictionaru that will override + the variables of this environment. + """ + + if overrides: + proxy = EnvProxy(self._dict) + proxy.update(overrides) + return proxy + else: + return self + + def get(self, key, default): + "Emulates the get() method of dictionaries.""" + return self._dict.get(key, default) + class VarInterpolator: def __init__(self, dest, src, prefix, suffix): self.dest = dest diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 1b2cd3b..70e28ff 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -94,6 +94,14 @@ class Scanner: class EnvironmentTestCase(unittest.TestCase): + def test_Override(self): + env = Environment(ONE=1, TWO=2) + assert env['ONE'] == 1 + assert env['TWO'] == 2 + env2 = env.Override({'TWO':'10'}) + assert env2['ONE'] == 1 + assert env2['TWO'] == '10' + def test_Builder_calls(self): """Test Builder calls through different environments """ diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 707d4a2..1d8b0eb 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -40,7 +40,7 @@ class Builder: def __init__(self, factory): self.factory = factory - def execute(self, **kw): + def execute(self, target, source, env): global built_it built_it = 1 return 0 @@ -69,6 +69,8 @@ class Environment: return {} def get_scanner(self, skey): return self.scanner + def Override(self, overrides): + return self class BuildDirTestCase(unittest.TestCase): def runTest(self): @@ -103,6 +105,7 @@ class BuildDirTestCase(unittest.TestCase): fs.BuildDir('../var2', 'src') f1 = fs.File('../var1/test1') f2 = fs.File('../var2/test1') + assert hasattr(f1, 'overrides') assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath assert f2.srcpath == os.path.normpath('src/test1'), f2.srcpath diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 31c4ee6..8897b00 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -39,42 +39,42 @@ built_source = None cycle_detected = None class Builder: - def execute(self, **kw): + def execute(self, target, source, env): global built_it, built_target, built_source, built_args built_it = 1 - built_target = kw['target'] - built_source = kw['source'] - built_args = kw + built_target = target + built_source = source + built_args = env return 0 - def get_contents(self, env, target, source): + def get_contents(self, target, source, env): return 7 class NoneBuilder(Builder): - def execute(self, **kw): - apply(Builder.execute, (self,), kw) + def execute(self, target, source, env): + Builder.execute(self, target, source, env) return None class ListBuilder(Builder): def __init__(self, *nodes): self.nodes = nodes - def execute(self, **kw): + def execute(self, target, source, env): if hasattr(self, 'status'): return self.status for n in self.nodes: n.prepare() - kw['target'] = self.nodes[0] - self.status = apply(Builder.execute, (self,), kw) + target = self.nodes[0] + self.status = Builder.execute(self, target, source, env) class FailBuilder: - def execute(self, **kw): + def execute(self, target, source, env): return 1 class ExceptBuilder: - def execute(self, **kw): + def execute(self, target, source, env): raise SCons.Errors.BuildError class ExceptBuilder2: - def execute(self, **kw): + def execute(self, target, source, env): raise "foo" class Environment: @@ -82,6 +82,8 @@ class Environment: return {} def autogenerate(self, **kw): return {} + def Override(selv, overrides): + return overrides @@ -161,7 +163,7 @@ class NodeTestCase(unittest.TestCase): node.env_set(Environment()) node.path = "qqq" node.sources = ["rrr", "sss"] - node.build_args = { "foo" : 1, "bar" : 2 } + node.overrides = { "foo" : 1, "bar" : 2 } node.build() assert built_it assert type(built_target) == type(MyNode()), type(built_target) diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 09ceea0..b2bd8ed 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -87,24 +87,21 @@ class Node: self.precious = None self.found_includes = {} self.includes = None - self.build_args = {} + self.overrides = {} # construction variable overrides for building this node self.attributes = self.Attrs() # Generic place to stick information about the Node. self.side_effect = 0 # true iff this node is a side effect self.side_effects = [] # the side effects of building this target - def generate_build_args(self): - dict = copy.copy(self.env.Dictionary()) + def generate_build_env(self): if hasattr(self, 'cwd'): auto = self.env.autogenerate(dir = self.cwd) else: auto = self.env.autogenerate() - dict.update(auto) - dictArgs = { 'env' : dict, - 'target' : self, - 'source' : self.sources } - dictArgs.update(self.build_args) - return dictArgs + dict = {} + dict.update(auto) + dict.update(self.overrides) + return self.env.Override(dict) def build(self): """Actually build the node. Return the status from the build.""" @@ -118,8 +115,7 @@ class Node: stat = self.builder.status except AttributeError: try: - stat = apply(self.builder.execute, (), - self.generate_build_args()) + stat = self.builder.execute(self, self.sources, self.generate_build_env()) except KeyboardInterrupt: raise except UserError: @@ -180,8 +176,7 @@ class Node: def __init__(self, node): self.node = node def get_contents(self): - return apply(self.node.builder.get_contents, (), - self.node.generate_build_args()) + return self.node.builder.get_contents(self.node, self.node.sources, self.node.generate_build_env()) def get_timestamp(self): return None return Adapter(self) @@ -210,7 +205,7 @@ class Node: if implicit_deps_unchanged or calc.current(self, calc.bsig(self)): return else: - # one of this node's sources has changed, so + # one of this node's sources has changed, so # we need to recalculate the implicit deps, # and the bsig: self.implicit = [] diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 9d399cd..35bd868 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -65,14 +65,15 @@ def win32TempFileMunge(env, cmd_list, for_signature): return [ [cmd[0], '@' + tmp], ['del', tmp] ] -def win32LinkGenerator(env, target, source, for_signature, **kw): +def win32LinkGenerator(env, target, source, for_signature): args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0], '$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ] args.extend(map(SCons.Util.to_String, source)) return win32TempFileMunge(env, args, for_signature) -def win32LibGenerator(target, source, env, for_signature, no_import_lib=0): +def win32LibGenerator(target, source, env, for_signature): listCmd = [ "$SHLINK", "$SHLINKFLAGS" ] + no_import_lib = env.get('no_import_lib', 0) for tgt in target: ext = os.path.splitext(str(tgt))[1] @@ -95,8 +96,10 @@ def win32LibGenerator(target, source, env, for_signature, no_import_lib=0): listCmd.append(str(src)) return win32TempFileMunge(env, listCmd, for_signature) -def win32LibEmitter(target, source, env, no_import_lib=0): +def win32LibEmitter(target, source, env): dll = None + no_import_lib = env.get('no_import_lib', 0) + for tgt in target: ext = os.path.splitext(str(tgt))[1] if ext == env.subst("$SHLIBSUFFIX"): |