summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/engine/SCons/Action.py236
-rw-r--r--src/engine/SCons/ActionTests.py193
-rw-r--r--src/engine/SCons/Builder.py6
-rw-r--r--src/engine/SCons/Executor.py52
-rw-r--r--src/engine/SCons/ExecutorTests.py98
-rw-r--r--src/engine/SCons/Node/FS.py19
-rw-r--r--src/engine/SCons/Node/FSTests.py5
-rw-r--r--src/engine/SCons/Node/NodeTests.py6
-rw-r--r--src/engine/SCons/Node/__init__.py30
-rw-r--r--test/strfunction.py143
10 files changed, 463 insertions, 325 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 37665b9..c95644a 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -51,7 +51,7 @@ this module:
This is used by the ActionBase.show() command to display the
command/function that will be executed to generate the target(s).
- _execute()
+ execute()
The internal method that really, truly, actually handles the
execution of a command or Python function. This is used so
that the __call__() methods can take care of displaying any
@@ -207,29 +207,53 @@ def Action(act, strfunction=_null, varlist=[]):
class ActionBase:
"""Base class for actions that create output objects."""
+ def __init__(self, strfunction=_null, **kw):
+ if not strfunction is _null:
+ self.strfunction = strfunction
+
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
- def show(self, s):
- if print_actions:
- sys.stdout.write(s + '\n')
-
- def presub(self, target, env):
- if print_actions_presub:
- if not SCons.Util.is_List(target):
- target = [target]
- # CommandGeneratorAction needs a real environment
- # in order to return the proper string here, since
- # it may call LazyCmdGenerator, which looks up a key
- # in that env. So we temporarily remember the env here,
- # and CommandGeneratorAction will use this env
- # when it calls its __generate method.
- self.presub_env = env
- lines = string.split(str(self), '\n')
- self.presub_env = None # don't need this any more
- sys.stdout.write("Building %s with action(s):\n %s\n"%
- (string.join(map(lambda x: str(x), target), ' and '),
- string.join(lines, '\n ')))
+ def __call__(self, target, source, env,
+ errfunc=None,
+ presub=_null,
+ show=_null,
+ execute=_null):
+ if not SCons.Util.is_List(target):
+ target = [target]
+ if not SCons.Util.is_List(source):
+ source = [source]
+ if presub is _null: presub = print_actions_presub
+ if show is _null: show = print_actions
+ if execute is _null: execute = execute_actions
+ if presub:
+ t = string.join(map(str, target), 'and')
+ l = string.join(self.presub(env), '\n ')
+ out = "Building %s with action(s):\n %s\n" % (t, l)
+ sys.stdout.write(out)
+ if show and self.strfunction:
+ s = self.strfunction(target, source, env)
+ if s:
+ sys.stdout.write(s + '\n')
+ if execute:
+ stat = self.execute(target, source, env)
+ if stat and errfunc:
+ errfunc(stat)
+ return stat
+ else:
+ return 0
+
+ def presub(self, env):
+ # CommandGeneratorAction needs a real environment
+ # in order to return the proper string here, since
+ # it may call LazyCmdGenerator, which looks up a key
+ # in that env. So we temporarily remember the env here,
+ # and CommandGeneratorAction will use this env
+ # when it calls its __generate method.
+ self.presub_env = env
+ lines = string.split(str(self), '\n')
+ self.presub_env = None # don't need this any more
+ return lines
def genstring(self, target, source, env):
return str(self)
@@ -255,23 +279,22 @@ def _string_from_cmd_list(cmd_list):
class CommandAction(ActionBase):
"""Class for command-execution actions."""
- def __init__(self, cmd, strfunction=_null, varlist=[]):
+ def __init__(self, cmd, **kw):
# Cmd list can actually be a list or a single item...basically
# anything that we could pass in as the first arg to
# Environment.subst_list().
if __debug__: logInstanceCreation(self)
+ apply(ActionBase.__init__, (self,), kw)
self.cmd_list = cmd
- if not strfunction is _null:
- self.strfunction = strfunction
def __str__(self):
return str(self.cmd_list)
def strfunction(self, target, source, env):
cmd_list = env.subst_list(self.cmd_list, 0, target, source)
- return map(_string_from_cmd_list, cmd_list)
+ return string.join(map(_string_from_cmd_list, cmd_list), "\n")
- def _execute(self, target, source, env):
+ def execute(self, target, source, env):
"""Execute a command action.
This will handle lists of commands as well as individual commands,
@@ -315,50 +338,43 @@ class CommandAction(ActionBase):
cmd_list = env.subst_list(self.cmd_list, 0, target, source)
for cmd_line in cmd_list:
if len(cmd_line):
- if print_actions:
- self.show(_string_from_cmd_list(cmd_line))
- if execute_actions:
- try:
- ENV = env['ENV']
- except KeyError:
- global default_ENV
- if not default_ENV:
- import SCons.Environment
- default_ENV = SCons.Environment.Environment()['ENV']
- ENV = default_ENV
-
- # ensure that the ENV values are all strings:
- for key, value in ENV.items():
- if SCons.Util.is_List(value):
- # If the value is a list, then we assume
- # it is a path list, because that's a pretty
- # common list like value to stick in an environment
- # variable:
- ENV[key] = string.join(map(str, value), os.pathsep)
- elif not SCons.Util.is_String(value):
- # If it isn't a string or a list, then
- # we just coerce it to a string, which
- # is proper way to handle Dir and File instances
- # and will produce something reasonable for
- # just about everything else:
- ENV[key] = str(value)
-
- # Escape the command line for the command
- # interpreter we are using
- cmd_line = SCons.Util.escape_list(cmd_line, escape)
- if pipe_build:
- ret = pspawn( shell, escape, cmd_line[0], cmd_line,
- ENV, pstdout, pstderr )
- else:
- ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
- if ret:
- return ret
+ try:
+ ENV = env['ENV']
+ except KeyError:
+ global default_ENV
+ if not default_ENV:
+ import SCons.Environment
+ default_ENV = SCons.Environment.Environment()['ENV']
+ ENV = default_ENV
+
+ # ensure that the ENV values are all strings:
+ for key, value in ENV.items():
+ if SCons.Util.is_List(value):
+ # If the value is a list, then we assume
+ # it is a path list, because that's a pretty
+ # common list like value to stick in an environment
+ # variable:
+ ENV[key] = string.join(map(str, value), os.pathsep)
+ elif not SCons.Util.is_String(value):
+ # If it isn't a string or a list, then
+ # we just coerce it to a string, which
+ # is proper way to handle Dir and File instances
+ # and will produce something reasonable for
+ # just about everything else:
+ ENV[key] = str(value)
+
+ # Escape the command line for the command
+ # interpreter we are using
+ cmd_line = SCons.Util.escape_list(cmd_line, escape)
+ if pipe_build:
+ ret = pspawn( shell, escape, cmd_line[0], cmd_line,
+ ENV, pstdout, pstderr )
+ else:
+ ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
+ if ret:
+ return ret
return 0
- def __call__(self, target, source, env):
- self.presub(target, env)
- return self._execute(target, source, env)
-
def get_contents(self, target, source, env, dict=None):
"""Return the signature contents of this action's command line.
@@ -374,11 +390,10 @@ class CommandAction(ActionBase):
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
- def __init__(self, generator, strfunction=_null, varlist=[]):
+ def __init__(self, generator, **kw):
if __debug__: logInstanceCreation(self)
+ apply(ActionBase.__init__, (self,), kw)
self.generator = generator
- if not strfunction is _null:
- self.strfunction = strfunction
def __generate(self, target, source, env, for_signature):
# ensure that target is a list, to make it easier to write
@@ -410,20 +425,10 @@ class CommandGeneratorAction(ActionBase):
def genstring(self, target, source, env):
return str(self.__generate(target, source, env, 0))
- def _execute(self, target, source, env):
- if not SCons.Util.is_List(source):
- source = [source]
- rsources = map(rfile, source)
- act = self.__generate(target, source, env, 0)
- return act._execute(target, rsources, env)
-
- def __call__(self, target, source, env):
- if not SCons.Util.is_List(source):
- source = [source]
+ def execute(self, target, source, env):
rsources = map(rfile, source)
act = self.__generate(target, source, env, 0)
- act.presub(target, env)
- return act._execute(target, source, env)
+ return act.execute(target, source, env)
def get_contents(self, target, source, env, dict=None):
"""Return the signature contents of this action's command line.
@@ -453,37 +458,24 @@ class LazyCmdGenerator:
def __str__(self):
return 'LazyCmdGenerator: %s'%str(self.var)
- def _execute(self, target, source, env, for_signature):
+ def __call__(self, target, source, env, for_signature):
try:
return env[self.var]
except KeyError:
# The variable reference substitutes to nothing.
return ''
- def __call__(self, target, source, env, for_signature):
- return self._execute(target, source, env, for_signature)
-
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
class FunctionAction(ActionBase):
"""Class for Python function actions."""
- def __init__(self, execfunction, strfunction=_null, varlist=[]):
+ def __init__(self, execfunction, **kw):
if __debug__: logInstanceCreation(self)
self.execfunction = execfunction
- if strfunction is _null:
- def strfunction(target, source, env, self=self):
- def quote(s):
- return '"' + str(s) + '"'
- def array(a, q=quote):
- return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
- name = self.function_name()
- tstr = len(target) == 1 and quote(target[0]) or array(target)
- sstr = len(source) == 1 and quote(source[0]) or array(source)
- return "%s(%s, %s)" % (name, tstr, sstr)
- self.strfunction = strfunction
- self.varlist = varlist
+ apply(ActionBase.__init__, (self,), kw)
+ self.varlist = kw.get('varlist', [])
def function_name(self):
try:
@@ -494,27 +486,22 @@ class FunctionAction(ActionBase):
except AttributeError:
return "unknown_python_function"
+ def strfunction(self, target, source, env):
+ def quote(s):
+ return '"' + str(s) + '"'
+ def array(a, q=quote):
+ return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
+ name = self.function_name()
+ tstr = len(target) == 1 and quote(target[0]) or array(target)
+ sstr = len(source) == 1 and quote(source[0]) or array(source)
+ return "%s(%s, %s)" % (name, tstr, sstr)
+
def __str__(self):
return "%s(env, target, source)" % self.function_name()
- def _execute(self, target, source, env):
- r = 0
- if not SCons.Util.is_List(target):
- target = [target]
- if not SCons.Util.is_List(source):
- source = [source]
- if print_actions and self.strfunction:
- s = self.strfunction(target, source, env)
- if s:
- self.show(s)
- if execute_actions:
- rsources = map(rfile, source)
- r = self.execfunction(target=target, source=rsources, env=env)
- return r
-
- def __call__(self, target, source, env):
- self.presub(target, env)
- return self._execute(target, source, env)
+ def execute(self, target, source, env):
+ rsources = map(rfile, source)
+ return self.execfunction(target=target, source=rsources, env=env)
def get_contents(self, target, source, env, dict=None):
"""Return the signature contents of this callable action.
@@ -543,11 +530,10 @@ class FunctionAction(ActionBase):
class ListAction(ActionBase):
"""Class for lists of other actions."""
- def __init__(self, list, strfunction=_null, varlist=[]):
+ def __init__(self, list, **kw):
if __debug__: logInstanceCreation(self)
+ apply(ActionBase.__init__, (self,), kw)
self.list = map(lambda x: Action(x), list)
- if not strfunction is _null:
- self.strfunction = strfunction
def get_actions(self):
return self.list
@@ -568,17 +554,13 @@ class ListAction(ActionBase):
s.extend(x)
return string.join(s, "\n")
- def _execute(self, target, source, env):
+ def execute(self, target, source, env):
for l in self.list:
- r = l._execute(target, source, env)
+ r = l.execute(target, source, env)
if r:
return r
return 0
- def __call__(self, target, source, env):
- self.presub(target, env)
- return self._execute(target, source, env)
-
def get_contents(self, target, source, env, dict=None):
"""Return the signature contents of this action list.
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 3def857..b3d7e8c 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -320,6 +320,26 @@ class ActionTestCase(unittest.TestCase):
class ActionBaseTestCase(unittest.TestCase):
+ def test__init__(self):
+ """Test creation of ActionBase objects
+ """
+
+ def func():
+ pass
+
+ a = SCons.Action.ActionBase()
+ assert not hasattr(a, 'strfunction')
+
+ assert SCons.Action.ActionBase(kwarg = 1)
+ assert not hasattr(a, 'strfunction')
+ assert not hasattr(a, 'kwarg')
+
+ a = SCons.Action.ActionBase(func)
+ assert a.strfunction is func, a.strfunction
+
+ a = SCons.Action.ActionBase(strfunction=func)
+ assert a.strfunction is func, a.strfunction
+
def test___cmp__(self):
"""Test Action comparison
"""
@@ -330,112 +350,115 @@ class ActionBaseTestCase(unittest.TestCase):
assert a1 != a3
assert a2 != a3
- def test_show(self):
- """Test the show() method
+ def test___call__(self):
+ """Test calling an Action
"""
save_stdout = sys.stdout
save_print_actions = SCons.Action.print_actions
- SCons.Action.print_actions = 0
-
- try:
- a = SCons.Action.Action("x")
-
- sio = StringIO.StringIO()
- sys.stdout = sio
- a.show("xyzzy")
- s = sio.getvalue()
- assert s == "", s
-
- SCons.Action.print_actions = 1
-
- sio = StringIO.StringIO()
- sys.stdout = sio
- a.show("foobar")
- s = sio.getvalue()
- assert s == "foobar\n", s
-
- finally:
- SCons.Action.print_actions = save_print_actions
- sys.stdout = save_stdout
-
- def test_presub(self):
- """Test the presub() method
- """
- save_stdout = sys.stdout
-
save_print_actions_presub = SCons.Action.print_actions_presub
- SCons.Action.print_actions_presub = 0
+ save_execute_actions = SCons.Action.execute_actions
+ #SCons.Action.print_actions = 0
try:
- a = SCons.Action.Action("x")
env = Environment()
- sio = StringIO.StringIO()
- sys.stdout = sio
- a.presub("xyzzy", env)
- s = sio.getvalue()
- assert s == "", s
-
- SCons.Action.print_actions_presub = 1
+ def execfunc(target, source, env):
+ assert type(target) is type([]), type(target)
+ assert type(source) is type([]), type(source)
+ return 7
+ a = SCons.Action.Action(execfunc)
sio = StringIO.StringIO()
sys.stdout = sio
- a.presub("foobar", env)
+ result = a("out", "in", env)
+ assert result == 7, result
s = sio.getvalue()
- assert s == "Building foobar with action(s):\n x\n", s
+ assert s == 'execfunc("out", "in")\n', s
- a = SCons.Action.Action(["y", "z"])
+ SCons.Action.execute_actions = 0
sio = StringIO.StringIO()
sys.stdout = sio
- a.presub("foobar", env)
+ result = a("out", "in", env)
+ assert result == 0, result
s = sio.getvalue()
- assert s == "Building foobar with action(s):\n y\n z\n", s
+ assert s == 'execfunc("out", "in")\n', s
- def func():
- pass
- a = SCons.Action.Action(func)
+ SCons.Action.print_actions_presub = 1
sio = StringIO.StringIO()
sys.stdout = sio
- a.presub("foobar", env)
+ result = a("out", "in", env)
+ assert result == 0, result
s = sio.getvalue()
- assert s == "Building foobar with action(s):\n func(env, target, source)\n", s
-
- def gen(target, source, env, for_signature):
- return 'generat' + env.get('GEN', 'or')
- a = SCons.Action.Action(SCons.Action.CommandGenerator(gen))
+ assert s == 'Building out with action(s):\n execfunc(env, target, source)\nexecfunc("out", "in")\n', s
sio = StringIO.StringIO()
sys.stdout = sio
- a.presub("foobar", env)
+ result = a("out", "in", env, presub=0)
+ assert result == 0, result
s = sio.getvalue()
- assert s == "Building foobar with action(s):\n generator\n", s
+ assert s == 'execfunc("out", "in")\n', s
sio = StringIO.StringIO()
sys.stdout = sio
- a.presub("foobar", Environment(GEN = 'ed'))
+ result = a("out", "in", env, presub=0, execute=1, show=0)
+ assert result == 7, result
s = sio.getvalue()
- assert s == "Building foobar with action(s):\n generated\n", s
+ assert s == '', s
+
+ sys.stdout = save_stdout
+ errfunc_result = []
- a = SCons.Action.Action("$ACT")
+ def errfunc(stat, result=errfunc_result):
+ result.append(stat)
- sio = StringIO.StringIO()
- sys.stdout = sio
- a.presub("foobar", env)
- s = sio.getvalue()
- assert s == "Building foobar with action(s):\n \n", s
+ result = a("out", "in", env, errfunc=errfunc)
+ assert result == 0, result
+ assert errfunc_result == [], errfunc_result
- sio = StringIO.StringIO()
- sys.stdout = sio
- a.presub("foobar", Environment(ACT = 'expanded action'))
- s = sio.getvalue()
- assert s == "Building foobar with action(s):\n expanded action\n", s
+ result = a("out", "in", env, execute=1, errfunc=errfunc)
+ assert result == 7, result
+ assert errfunc_result == [7], errfunc_result
finally:
- SCons.Action.print_actions_presub = save_print_actions_presub
sys.stdout = save_stdout
+ SCons.Action.print_actions = save_print_actions
+ SCons.Action.print_actions_presub = save_print_actions_presub
+ SCons.Action.execute_actions = save_execute_actions
+
+ def test_presub(self):
+ """Test the presub() method
+ """
+ env = Environment()
+ a = SCons.Action.Action("x")
+ s = a.presub(env)
+ assert s == ['x'], s
+
+ a = SCons.Action.Action(["y", "z"])
+ s = a.presub(env)
+ assert s == ['y', 'z'], s
+
+ def func():
+ pass
+ a = SCons.Action.Action(func)
+ s = a.presub(env)
+ assert s == ["func(env, target, source)"], s
+
+ def gen(target, source, env, for_signature):
+ return 'generat' + env.get('GEN', 'or')
+ a = SCons.Action.Action(SCons.Action.CommandGenerator(gen))
+ s = a.presub(env)
+ assert s == ["generator"], s
+ s = a.presub(Environment(GEN = 'ed'))
+ assert s == ["generated"], s
+
+ a = SCons.Action.Action("$ACT")
+ s = a.presub(env)
+ assert s == [''], s
+ s = a.presub(Environment(ACT = 'expanded action'))
+ assert s == ['expanded action'], s
def test_get_actions(self):
"""Test the get_actions() method
@@ -592,29 +615,29 @@ class CommandActionTestCase(unittest.TestCase):
s2 = DummyNode('s2')
act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
s = act.strfunction([], [], env)
- assert s == ['xyzzy'], s
+ assert s == 'xyzzy', s
s = act.strfunction([t1], [s1], env)
- assert s == ['xyzzy t1 s1'], s
+ assert s == 'xyzzy t1 s1', s
s = act.strfunction([t1, t2], [s1, s2], env)
- assert s == ['xyzzy t1 s1'], s
+ assert s == 'xyzzy t1 s1', s
act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
s = act.strfunction([], [], env)
- assert s == ['xyzzy'], s
+ assert s == 'xyzzy', s
s = act.strfunction([t1], [s1], env)
- assert s == ['xyzzy t1 s1'], s
+ assert s == 'xyzzy t1 s1', s
s = act.strfunction([t1, t2], [s1, s2], env)
- assert s == ['xyzzy t1 t2 s1 s2'], s
+ assert s == 'xyzzy t1 t2 s1 s2', s
act = SCons.Action.CommandAction(['xyzzy',
'$TARGET', '$SOURCE',
'$TARGETS', '$SOURCES'])
s = act.strfunction([], [], env)
- assert s == ['xyzzy'], s
+ assert s == 'xyzzy', s
s = act.strfunction([t1], [s1], env)
- assert s == ['xyzzy t1 s1 t1 s1'], s
+ assert s == 'xyzzy t1 s1 t1 s1', s
s = act.strfunction([t1, t2], [s1, s2], env)
- assert s == ['xyzzy t1 s1 t1 t2 s1 s2'], s
+ assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s
def sf(target, source, env):
return "sf was called"
@@ -975,7 +998,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
self.dummy = 0
s = a.strfunction([], [], env=Environment(FOO='xyzzy', dummy=1))
assert self.dummy == 1, self.dummy
- assert s == ['xyzzy'], s
+ assert s == 'xyzzy', s
def sf(target, source, env):
return "sf was called"
@@ -1075,20 +1098,12 @@ class FunctionActionTestCase(unittest.TestCase):
a = SCons.Action.FunctionAction(func1)
assert a.execfunction == func1, a.execfunction
- assert isinstance(a.strfunction, types.FunctionType)
+ assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
a = SCons.Action.FunctionAction(func2, strfunction=func3)
assert a.execfunction == func2, a.execfunction
assert a.strfunction == func3, a.strfunction
- a = SCons.Action.FunctionAction(func3, func4)
- assert a.execfunction == func3, a.execfunction
- assert a.strfunction == func4, a.strfunction
-
- a = SCons.Action.FunctionAction(func4, None)
- assert a.execfunction == func4, a.execfunction
- assert a.strfunction is None, a.strfunction
-
def test___str__(self):
"""Test the __str__() method for function Actions
"""
@@ -1172,7 +1187,7 @@ class FunctionActionTestCase(unittest.TestCase):
def string_it(target, source, env, self=self):
self.string_it = 1
return None
- act = SCons.Action.FunctionAction(build_it, string_it)
+ act = SCons.Action.FunctionAction(build_it, strfunction=string_it)
r = act([], [], Environment())
assert r == 0, r
assert self.build_it
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 4bca1c8..55ea07f 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -324,9 +324,9 @@ def _init_nodes(builder, env, overrides, tlist, slist):
else:
executor.add_sources(slist)
if executor is None:
- executor = SCons.Executor.Executor(builder,
- env,
- overrides,
+ executor = SCons.Executor.Executor(builder.action,
+ env or builder.env,
+ [builder.overrides, overrides],
tlist,
slist)
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index ac209e0..b3e6f88 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -38,16 +38,16 @@ import SCons.Util
class Executor:
"""A class for controlling instances of executing an action.
- This largely exists to hold a single association of a builder,
- environment, environment overrides, targets and sources for later
- processing as needed.
+ This largely exists to hold a single association of an action,
+ environment, list of environment override dictionaries, targets
+ and sources for later processing as needed.
"""
- def __init__(self, builder, env, overrides, targets, sources):
+ def __init__(self, action, env=None, overridelist=[], targets=[], sources=[]):
if __debug__: logInstanceCreation(self)
- self.builder = builder
+ self.action = action
self.env = env
- self.overrides = overrides
+ self.overridelist = overridelist
self.targets = targets
self.sources = sources[:]
@@ -58,32 +58,23 @@ class Executor:
try:
return self.build_env
except AttributeError:
- if self.env is None:
- # There was no Environment specifically associated with
- # this set of targets (which kind of implies that it
- # is--or they are--source files, but who knows...).
- # So use the environment associated with the Builder
- # itself.
- env = self.builder.env
- else:
- # The normal case: use the Environment that was
- # used to specify how these targets will be built.
- env = self.env
-
# Create the build environment instance with appropriate
# overrides. These get evaluated against the current
# environment's construction variables so that users can
# add to existing values by referencing the variable in
# the expansion.
overrides = {}
- overrides.update(self.builder.overrides)
- overrides.update(self.overrides)
+ for odict in self.overridelist:
+ overrides.update(odict)
try:
generate_build_dict = self.targets[0].generate_build_dict
- except AttributeError:
+ except (AttributeError, IndexError):
pass
else:
overrides.update(generate_build_dict())
+
+ import SCons.Defaults
+ env = self.env or SCons.Defaults.DefaultEnvironment()
self.build_env = env.Override(overrides)
# Now update the build environment with the things that we
@@ -106,19 +97,22 @@ class Executor:
try:
al = self.action_list
except AttributeError:
- al = self.builder.action.get_actions()
+ al = self.action.get_actions()
self.action_list = al
- # XXX shouldn't reach into node attributes like this
- return target.pre_actions + al + target.post_actions
+ try:
+ # XXX shouldn't reach into node attributes like this
+ return target.pre_actions + al + target.post_actions
+ except AttributeError:
+ return al
- def __call__(self, target, func):
+ def __call__(self, target, errfunc, **kw):
"""Actually execute the action list."""
action_list = self.get_action_list(target)
if not action_list:
return
env = self.get_build_env()
for action in action_list:
- func(action, self.targets, self.sources, env)
+ apply(action, (self.targets, self.sources, env, errfunc), kw)
def cleanup(self):
try:
@@ -137,7 +131,7 @@ class Executor:
try:
return self.string
except AttributeError:
- action = self.builder.action
+ action = self.action
self.string = action.genstring(self.targets,
self.sources,
self.get_build_env())
@@ -152,7 +146,7 @@ class Executor:
try:
return self.raw_contents
except AttributeError:
- action = self.builder.action
+ action = self.action
self.raw_contents = action.get_raw_contents(self.targets,
self.sources,
self.get_build_env())
@@ -167,7 +161,7 @@ class Executor:
try:
return self.contents
except AttributeError:
- action = self.builder.action
+ action = self.action
self.contents = action.get_contents(self.targets,
self.sources,
self.get_build_env())
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
index 23b7719..c4a5552 100644
--- a/src/engine/SCons/ExecutorTests.py
+++ b/src/engine/SCons/ExecutorTests.py
@@ -44,7 +44,8 @@ class MyEnvironment:
self._dict.update(dict)
class MyAction:
- actions = ['action1', 'action2']
+ def __init__(self, actions=['action1', 'action2']):
+ self.actions = actions
def get_actions(self):
return self.actions
def genstring(self, target, source, env):
@@ -71,28 +72,25 @@ class ExecutorTestCase(unittest.TestCase):
def test__init__(self):
"""Test creating an Executor"""
source_list = ['s1', 's2']
- x = SCons.Executor.Executor('b', 'e', 'o', 't', source_list)
- assert x.builder == 'b', x.builder
+ x = SCons.Executor.Executor('a', 'e', ['o'], 't', source_list)
+ assert x.action == 'a', x.builder
assert x.env == 'e', x.env
- assert x.overrides == 'o', x.overrides
+ assert x.overridelist == ['o'], x.overridelist
assert x.targets == 't', x.targets
source_list.append('s3')
assert x.sources == ['s1', 's2'], x.sources
def test_get_build_env(self):
"""Test fetching and generating a build environment"""
- x = SCons.Executor.Executor(MyBuilder('e', {}),
- 'e',
- {},
- 't',
- ['s1', 's2'])
+ x = SCons.Executor.Executor(MyAction(), 'e', [], 't', ['s1', 's2'])
x.build_env = 'eee'
be = x.get_build_env()
assert be == 'eee', be
- x = SCons.Executor.Executor(MyBuilder('e', {}),
- MyEnvironment(X='xxx'),
- {'O':'o2'},
+ env = MyEnvironment(X='xxx')
+ x = SCons.Executor.Executor(MyAction(),
+ env,
+ [{'O':'o2'}],
't',
['s1', 's2'])
be = x.get_build_env()
@@ -100,19 +98,13 @@ class ExecutorTestCase(unittest.TestCase):
assert be['X'] == 'xxx', be['X']
env = MyEnvironment(Y='yyy')
- x = SCons.Executor.Executor(MyBuilder(env, {'O':'ob3'}),
- None,
- {'O':'oo3'},
- 't',
- 's')
+ overrides = [{'O':'ob3'}, {'O':'oo3'}]
+ x = SCons.Executor.Executor(MyAction(), env, overrides, 't', 's')
be = x.get_build_env()
assert be['O'] == 'oo3', be['O']
assert be['Y'] == 'yyy', be['Y']
- x = SCons.Executor.Executor(MyBuilder(env, {'O':'ob3'}),
- None,
- {},
- 't',
- 's')
+ overrides = [{'O':'ob3'}]
+ x = SCons.Executor.Executor(MyAction(), env, overrides, 't', 's')
be = x.get_build_env()
assert be['O'] == 'ob3', be['O']
assert be['Y'] == 'yyy', be['Y']
@@ -126,23 +118,51 @@ class ExecutorTestCase(unittest.TestCase):
al = x.get_action_list(MyNode(['PRE'], ['POST']))
assert al == ['PRE', 'aaa', 'POST'], al
- x = SCons.Executor.Executor(MyBuilder('e', 'o'), None, {}, 't', 's')
+ x = SCons.Executor.Executor(MyAction(), None, {}, 't', 's')
al = x.get_action_list(MyNode(['pre'], ['post']))
assert al == ['pre', 'action1', 'action2', 'post'], al
def test__call__(self):
"""Test calling an Executor"""
- actions = []
- env = MyEnvironment(CALL='call')
- b = MyBuilder(env, {})
- x = SCons.Executor.Executor(b, None, {}, ['t1', 't2'], ['s1', 's2'])
- def func(action, target, source, env, a=actions):
- a.append(action)
- assert target == ['t1', 't2'], target
- assert source == ['s1', 's2'], source
- assert env['CALL'] == 'call', env['CALL']
- x(MyNode(['pre'], ['post']), func)
- assert actions == ['pre', 'action1', 'action2', 'post'], actions
+ result = []
+ def pre(target, source, env, errfunc, result=result, **kw):
+ result.append('pre')
+ def action1(target, source, env, errfunc, result=result, **kw):
+ result.append('action1')
+ def action2(target, source, env, errfunc, result=result, **kw):
+ result.append('action2')
+ def post(target, source, env, errfunc, result=result, **kw):
+ result.append('post')
+
+ env = MyEnvironment()
+ a = MyAction([action1, action2])
+ x = SCons.Executor.Executor(a, env, [], ['t1', 't2'], ['s1', 's2'])
+
+ x(MyNode([pre], [post]), None)
+ assert result == ['pre', 'action1', 'action2', 'post'], result
+ del result[:]
+
+ def pre_err(target, source, env, errfunc, result=result, **kw):
+ result.append('pre_err')
+ if errfunc:
+ errfunc(1)
+ return 1
+
+ x(MyNode([pre_err], [post]), None)
+ assert result == ['pre_err', 'action1', 'action2', 'post'], result
+ del result[:]
+
+ def errfunc(stat):
+ raise "errfunc %s" % stat
+
+ try:
+ x(MyNode([pre_err], [post]), errfunc)
+ except:
+ assert sys.exc_type == "errfunc 1", sys.exc_type
+ else:
+ assert 0, "did not catch expected exception"
+ assert result == ['pre_err'], result
+ del result[:]
def test_cleanup(self):
"""Test cleaning up an Executor"""
@@ -171,7 +191,7 @@ class ExecutorTestCase(unittest.TestCase):
"""Test the __str__() method"""
env = MyEnvironment(S='string')
- x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
c = str(x)
assert c == 'GENSTRING action1 action2 t s', c
@@ -179,12 +199,12 @@ class ExecutorTestCase(unittest.TestCase):
"""Test fetching the raw signatures contents"""
env = MyEnvironment(RC='raw contents')
- x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
x.raw_contents = 'raw raw raw'
rc = x.get_raw_contents()
assert rc == 'raw raw raw', rc
- x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
rc = x.get_raw_contents()
assert rc == 'RAW action1 action2 t s', rc
@@ -192,12 +212,12 @@ class ExecutorTestCase(unittest.TestCase):
"""Test fetching the signatures contents"""
env = MyEnvironment(C='contents')
- x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
x.contents = 'contents'
c = x.get_contents()
assert c == 'contents', c
- x = SCons.Executor.Executor(MyBuilder(env, {}), None, {}, ['t'], ['s'])
+ x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
c = x.get_contents()
assert c == 'action1 action2 t s', c
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index a30994d..008976b 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -1476,7 +1476,7 @@ class File(Base):
listDirs.reverse()
for dirnode in listDirs:
try:
- Mkdir(dirnode, None, None)
+ Mkdir(dirnode, [], None)
# The Mkdir() action may or may not have actually
# created the directory, depending on whether the -n
# option was used or not. Delete the _exists and
@@ -1506,17 +1506,10 @@ class File(Base):
return None
if b and self.fs.CachePath:
if self.fs.cache_show:
- if CacheRetrieveSilent(self, None, None) == 0:
- def do_print(action, targets, sources, env, s=self):
- if action.strfunction:
- al = action.strfunction(targets, s.sources, env)
- if not SCons.Util.is_List(al):
- al = [al]
- for a in al:
- action.show(a)
- self._for_each_action(do_print)
+ if CacheRetrieveSilent(self, [], None) == 0:
+ self.build(presub=0, execute=0)
return 1
- elif CacheRetrieve(self, None, None) == 0:
+ elif CacheRetrieve(self, [], None) == 0:
return 1
return None
@@ -1526,7 +1519,7 @@ class File(Base):
# method has a chance to clear the build signature, which it
# will do if this file has a source scanner.
if self.fs.CachePath and self.fs.exists(self.path):
- CachePush(self, None, None)
+ CachePush(self, [], None)
SCons.Node.Node.built(self)
self.found_includes = {}
try:
@@ -1596,7 +1589,7 @@ class File(Base):
if self.exists():
if self.is_derived() and not self.precious:
try:
- Unlink(self, None, None)
+ Unlink(self, [], None)
except OSError, e:
raise SCons.Errors.BuildError(node = self,
errstr = e.strerror)
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index d5e04e1..9a5be45 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -76,9 +76,10 @@ class Environment:
pass
class Action:
- def __call__(self, targets, sources, env):
+ def __call__(self, targets, sources, env, errfunc, **kw):
global built_it
- built_it = 1
+ if kw.get('execute', 1):
+ built_it = 1
return 0
def show(self, string):
pass
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 4ea73ab..7a6c39a 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -43,7 +43,7 @@ class MyAction:
def __init__(self):
self.order = 0
- def __call__(self, target, source, env):
+ def __call__(self, target, source, env, errfunc):
global built_it, built_target, built_source, built_args, built_order
built_it = 1
built_target = target
@@ -63,7 +63,7 @@ class MyNonGlobalAction:
self.built_target = None
self.built_source = None
- def __call__(self, target, source, env):
+ def __call__(self, target, source, env, errfunc):
# Okay, so not ENTIRELY non-global...
global built_order
self.built_it = 1
@@ -166,6 +166,8 @@ class NodeTestCase(unittest.TestCase):
node = MyNode("www")
node.build()
assert built_it == None
+ node.build(extra_kw_argument = 1)
+ assert built_it == None
node = MyNode("xxx")
node.builder_set(Builder())
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 9897d1a..9a4162c 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -161,26 +161,14 @@ class Node:
if not create:
raise
import SCons.Executor
- executor = SCons.Executor.Executor(self.builder,
+ executor = SCons.Executor.Executor(self.builder.action,
self.builder.env,
- {},
+ [self.builder.overrides],
[self],
self.sources)
self.executor = executor
return executor
- def _for_each_action(self, func):
- """Call a function for each action required to build a node.
-
- The purpose here is to have one place for the logic that
- collects and executes all of the actions for a node's builder,
- even though multiple sections of code elsewhere need this logic
- to do different things."""
- if not self.has_builder():
- return
- executor = self.get_executor()
- executor(self, func)
-
def retrieve_from_cache(self):
"""Try to retrieve the node's content from a cache
@@ -192,19 +180,19 @@ class Node:
"""
return 0
- def build(self):
+ def build(self, **kw):
"""Actually build the node.
This method is called from multiple threads in a parallel build,
so only do thread safe stuff here. Do thread unsafe stuff in
built().
"""
- def do_action(action, targets, sources, env, s=self):
- stat = action(targets, sources, env)
- if stat:
- raise SCons.Errors.BuildError(node = s,
- errstr = "Error %d" % stat)
- self._for_each_action(do_action)
+ if not self.has_builder():
+ return
+ def errfunc(stat, node=self):
+ raise SCons.Errors.BuildError(node=node, errstr="Error %d" % stat)
+ executor = self.get_executor()
+ apply(executor, (self, errfunc), kw)
def built(self):
"""Called just after this node is sucessfully built."""
diff --git a/test/strfunction.py b/test/strfunction.py
new file mode 100644
index 0000000..4ed75ee
--- /dev/null
+++ b/test/strfunction.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__"
+
+"""
+Test how using strfunction() to report different types of
+"""
+
+import TestSCons
+
+python = TestSCons.python
+
+test = TestSCons.TestSCons()
+
+test.write('cat.py', """\
+import sys
+open(sys.argv[2], "wb").write(open(sys.argv[1], "rb").read())
+sys.exit(0)
+""")
+
+test.write('SConstruct', """\
+def strfunction(target, source, env):
+ t = str(target[0])
+ s = str(source[0])
+ return "Building %%s from %%s" %% (t, s)
+def func(target, source, env):
+ t = str(target[0])
+ s = str(source[0])
+ open(t, 'w').write(open(s, 'r').read())
+funcaction = Action(func, strfunction=strfunction)
+cmd = r"%s cat.py $SOURCE $TARGET"
+cmdaction = Action(cmd, strfunction=strfunction)
+list = [ r"%s cat.py $SOURCE .temp", r"%s cat.py .temp $TARGET" ]
+listaction = Action(list, strfunction=strfunction)
+lazy = '$LAZY'
+lazyaction = Action(lazy, strfunction=strfunction)
+dict = {
+ '.cmd' : cmd,
+ '.cmdstr' : cmdaction,
+ '.func' : func,
+ '.funcstr' : funcaction,
+ '.list' : list,
+ '.liststr' : listaction,
+ '.lazy' : lazy,
+ '.lazystr' : lazyaction,
+}
+env = Environment(BUILDERS = {
+ 'Cmd' : Builder(action=cmd),
+ 'CmdStr' : Builder(action=cmdaction),
+ 'Func' : Builder(action=func),
+ 'FuncStr' : Builder(action=funcaction),
+ 'Lazy' : Builder(action=lazy),
+ 'LazyStr' : Builder(action=lazyaction),
+ 'List' : Builder(action=list),
+ 'ListStr' : Builder(action=listaction),
+
+ 'Dict' : Builder(action=dict),
+ },
+ LAZY = r"%s cat.py $SOURCE $TARGET")
+env.Cmd('cmd.out', 'cmd.in')
+env.CmdStr('cmdstr.out', 'cmdstr.in')
+env.Func('func.out', 'func.in')
+env.FuncStr('funcstr.out', 'funcstr.in')
+env.Lazy('lazy.out', 'lazy.in')
+env.LazyStr('lazystr.out', 'lazystr.in')
+env.List('list.out', 'list.in')
+env.ListStr('liststr.out', 'liststr.in')
+
+env.Dict('dict1.out', 'dict1.cmd')
+env.Dict('dict2.out', 'dict2.cmdstr')
+env.Dict('dict3.out', 'dict3.func')
+env.Dict('dict4.out', 'dict4.funcstr')
+env.Dict('dict5.out', 'dict5.lazy')
+env.Dict('dict6.out', 'dict6.lazystr')
+env.Dict('dict7.out', 'dict7.list')
+env.Dict('dict8.out', 'dict8.liststr')
+""" % (python, python, python, python))
+
+test.write('func.in', "func.in\n")
+test.write('funcstr.in', "funcstr.in\n")
+test.write('cmd.in', "cmd.in\n")
+test.write('cmdstr.in', "cmdstr.in\n")
+test.write('lazy.in', "lazy.in\n")
+test.write('lazystr.in', "lazystr.in\n")
+test.write('list.in', "list.in\n")
+test.write('liststr.in', "liststr.in\n")
+
+test.write('dict1.cmd', "dict1.cmd\n")
+test.write('dict2.cmdstr', "dict2.cmdstr\n")
+test.write('dict3.func', "dict3.func\n")
+test.write('dict4.funcstr', "dict4.funcstr\n")
+test.write('dict5.lazy', "dict4.lazy\n")
+test.write('dict6.lazystr', "dict6.lazystr\n")
+test.write('dict7.list', "dict7.list\n")
+test.write('dict8.liststr', "dict8.liststr\n")
+
+test.run(arguments = '.', stdout=test.wrap_stdout("""\
+%s cat.py cmd.in cmd.out
+Building cmdstr.out from cmdstr.in
+%s cat.py dict1.cmd dict1.out
+Building dict2.out from dict2.cmdstr
+func("dict3.out", "dict3.func")
+Building dict4.out from dict4.funcstr
+%s cat.py dict5.lazy dict5.out
+Building dict6.out from dict6.lazystr
+%s cat.py dict7.list .temp
+%s cat.py .temp dict7.out
+Building dict8.out from dict8.liststr
+func("func.out", "func.in")
+Building funcstr.out from funcstr.in
+%s cat.py lazy.in lazy.out
+Building lazystr.out from lazystr.in
+%s cat.py list.in .temp
+%s cat.py .temp list.out
+Building liststr.out from liststr.in
+Building liststr.out from liststr.in
+""") % (python, python, python, python, python, python, python, python))
+# XXX The duplication of "Buiding liststr.out" above is WRONG!
+# A follow-on fix should take care of this.
+
+test.pass_test()