Support overriding construction variables in builder call. (Anthony Roach)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 27 Sep 2002 23:03:51 +0000 (23:03 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 27 Sep 2002 23:03:51 +0000 (23:03 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@470 fdb21ef1-2011-0410-befe-b5e4ea1792b1

13 files changed:
doc/man/scons.1
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Tool/mslink.py
test/multi.py
test/overrides.py [new file with mode: 0644]

index 7bb09bb6ca3262875ae8c27916c046fe3cb409cc..6ff35db227d2218efb7a5ad6a151b6fa4152d1f2 100644 (file)
@@ -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
index 7aecf00167d27248a288fca81a2324d18f405c84..fcd0ec98d927fbfadd47b4edee07a78b2b718437 100644 (file)
@@ -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
+
index 16bcd80d952d621b2bf47e40636f7aa60c5ade1e..6ce52d56219a8fd465acc8b8497b000deea14fa1 100644 (file)
@@ -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
 
 
index fe6283b7c5cf4e31556243da4dea3616e4824722..02f9e2d5efb28a69e1e6782173b04276b1e99f6a 100644 (file)
@@ -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):
index e862b0567b40076768972738c10bbc92b6aadca5..191740517c7195988317767162e5cee01e058874 100644 (file)
@@ -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:
index a512d93a1a49aa9a6623c4653f193daacadb45a5..24315a10306365fb17e5b9bdf2721d8eee8b1d08 100644 (file)
@@ -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
index 1b2cd3b5b1706b83a4132dcbe90f0780b9c350e8..70e28ffe2b3d157b1810041acc5893175f42bb92 100644 (file)
@@ -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
         """
index 707d4a28a6d3374d33d86e4e71134c6b15e1fb57..1d8b0ebefef2d0bccc682e009151b8cd206efd8d 100644 (file)
@@ -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
 
index 31c4ee615ad18f02355849839b1d18a6fb4069ec..8897b005ba4922b78ef117510ed580cf6b7e5b35 100644 (file)
@@ -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)
index 09ceea07f46c328d6efcc5d30febdbc49510c647..b2bd8ed8d01f25142c936aef18f418f85d52f79d 100644 (file)
@@ -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 = []
index 9d399cdf54d00d6cb50410e3859ba1945e54a494..35bd86860a14526e32a6005f43316cb8dbfdc780 100644 (file)
@@ -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"):
index f5201d97cdefd5888688be67b9dc236dcc12b1c7..79d463e49038db3c839b0d83bfc1211dd8391659 100644 (file)
@@ -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 (file)
index 0000000..0ec3874
--- /dev/null
@@ -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()
+
+