diff options
-rw-r--r-- | doc/man/scons.1 | 37 | ||||
-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 | ||||
-rw-r--r-- | test/multi.py | 2 | ||||
-rw-r--r-- | test/overrides.py | 77 |
13 files changed, 410 insertions, 264 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 7bb09bb..6ff35db 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -812,6 +812,22 @@ env.Program(source = 'bar.c') env.Program('bar.c') .EE +It is possible to override or add construction variables when calling a +builder by passing additional keyword arguments. These overriden or added +variables will only be in effect when building the target, so they will not +effect other parts of the build. For example, if you want to add additional +libraries for just one program: + +.ES +env.Program('hello', 'hello.c', LIBS=['gl', 'glut']) +.EE + +or generate a shared library with a nonstandard suffix: + +.ES +env.SharedLibrary('word', 'word.cpp', SHLIBSUFFIX='.ocx') +.EE + .B scons provides the following builders: @@ -2334,13 +2350,22 @@ an Action object (see the next section); or a list of any of the above. +An action function +takes three arguments: +.I source +- a list of source nodes, +.I target +- a list of target nodes, +.I env +- the construction environment. + .IP multi Specifies whether this builder is allowed to be called multiple times for the same target file(s). The default is 0, which means the builder can not be called multiple times for the same target file(s). Calling a builder multiple times for the same target simply adds additional source files to the target; it is not allowed to change the environment associated -with the target, specify addition build arguments, or associate a different +with the target, specify addition environment overrides, or associate a different builder with the target. .IP prefix @@ -2364,6 +2389,16 @@ the list of targets to be built by this builder, and the list of sources for this builder. This allows the target and source lists to be manipulated before the target(s) are actually built. + +The emitter function +takes three arguments: +.I source +- a list of source nodes, +.I target +- a list of target nodes, +.I env +- the construction environment. + Example: .ES 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"): diff --git a/test/multi.py b/test/multi.py index f5201d9..79d463e 100644 --- a/test/multi.py +++ b/test/multi.py @@ -83,7 +83,7 @@ env.B(target = 'foo.out', source = 'bar.in', foo=2) test.run(arguments='foo.out', status=2, stderr=""" -SCons error: Two different sets of build arguments were specified for the same target: foo.out +SCons error: Two different sets of overrides were specified for the same target: foo.out File "SConstruct", line 10, in ? """) diff --git a/test/overrides.py b/test/overrides.py new file mode 100644 index 0000000..0ec3874 --- /dev/null +++ b/test/overrides.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# Copyright (c) 2001, 2002 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons +import sys + + +test = TestSCons.TestSCons() + + +python = sys.executable + +test.write('SConstruct', """ +env = Environment(LIBS=['a']) +def build(target, source, env): + assert env['CC'] == 'mycc' + assert env['LIBS'] == ['a','b'] +builder = Builder(action=build) +env['BUILDERS']['Build'] = builder + +Default(env.Build('foo', 'bar', CC='mycc', LIBS = env['LIBS']+['b'])) +""") + +test.run() + +test.write('SConstruct', """ +env = Environment() +env.Program('hello', 'hello.c', + CC=r'%s mycc.py', + LINK=r'%s mylink.py', + OBJSUFFIX='.not_obj', + PROGSUFFIX='.not_exe') +"""%(python,python)) + +test.write('hello.c',"this ain't no c file!\n") + +test.write('mycc.py',""" +open('hello.not_obj', 'wt').write('this is no object file!') +""") + +test.write('mylink.py',""" +open('hello.not_exe', 'wt').write('this is not a program!') +""") + +test.run(arguments='hello.not_exe') + +assert test.read('hello.not_obj') == 'this is no object file!' +assert test.read('hello.not_exe') == 'this is not a program!' + +test.up_to_date(arguments='hello.not_exe') + +test.pass_test() + + |