From: stevenknight Date: Mon, 15 Nov 2004 01:48:36 +0000 (+0000) Subject: Investigate getting rid of LazyCmdGenerator X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2b43b5b3cfc49e181ac4ef093a1b7d2f253d7c7d;p=scons.git Investigate getting rid of LazyCmdGenerator git-svn-id: http://scons.tigris.org/svn/scons/trunk@1161 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 3b62702b..a8e6c5b2 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -185,8 +185,7 @@ def _do_create_action(act, *args, **kw): # of that Environment variable, so a user could put something # like a function or a CommandGenerator in that variable # instead of a string. - lcg = LazyCmdGenerator(var) - return apply(CommandGeneratorAction, (lcg,)+args, kw) + return apply(LazyAction, (var,)+args, kw) commands = string.split(str(act), '\n') if len(commands) == 1: return apply(CommandAction, (commands[0],)+args, kw) @@ -234,7 +233,7 @@ class ActionBase: # 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. + # 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 @@ -431,7 +430,7 @@ class CommandGeneratorAction(ActionBase): self.generator = generator self.gen_kw = kw - def __generate(self, target, source, env, for_signature): + def _generate(self, target, source, env, for_signature): # ensure that target is a list, to make it easier to write # generator functions: if not SCons.Util.is_List(target): @@ -448,15 +447,15 @@ class CommandGeneratorAction(ActionBase): env = self.presub_env or {} except AttributeError: env = {} - act = self.__generate([], [], env, 1) + act = self._generate([], [], env, 1) return str(act) def genstring(self, target, source, env): - return self.__generate(target, source, env, 1).genstring(target, source, env) + return self._generate(target, source, env, 1).genstring(target, source, env) def __call__(self, target, source, env, errfunc=None, presub=_null, show=_null, execute=_null, chdir=_null): - act = self.__generate(target, source, env, 0) + act = self._generate(target, source, env, 0) return act(target, source, env, errfunc, presub, show, execute, chdir) @@ -466,30 +465,55 @@ class CommandGeneratorAction(ActionBase): This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ - return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None) - -class LazyCmdGenerator: - """This is not really an Action, although it kind of looks like one. - This is really a simple callable class that acts as a command - generator. It holds on to a key into an Environment dictionary, - then waits until execution time to see what type it is, then tries - to create an Action out of it.""" - def __init__(self, var): + return self._generate(target, source, env, 1).get_contents(target, source, env, dict=None) + +# Ooh, polymorphism -- pretty scary, eh, kids? +# +# A LazyCmdAction is a kind of hybrid generator and command action for +# strings of the form "$VAR". These strings normally expand to other +# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also +# want to be able to replace them with functions in the construction +# environment. Consequently, we want lazy evaluation and creation of +# an Action in the case of the function, but that's overkill in the more +# normal case of expansion to other strings. +# +# So we do this with a subclass that's both a generator *and* +# a command action. The overridden methods all do a quick check +# of the construction variable, and if it's a string we just call +# the corresponding CommandAction method to do the heavy lifting. +# If not, then we call the same-named CommandGeneratorAction method. +# The CommandGeneratorAction methods work by using the overridden +# _generate() method, uses our own way of handling "generation" of an +# action based on what's in the construction variable. + +class LazyAction(CommandGeneratorAction, CommandAction): + def __init__(self, var, *args, **kw): if __debug__: logInstanceCreation(self) + apply(CommandAction.__init__, (self, '$'+var)+args, kw) self.var = SCons.Util.to_String(var) + self.gen_kw = kw - def __str__(self): - return 'LazyCmdGenerator: %s'%str(self.var) + def get_parent_class(self, env): + c = env.get(self.var) + if SCons.Util.is_String(c) and not '\n' in c: + return CommandAction + return CommandGeneratorAction - def __call__(self, target, source, env, for_signature): - try: - return env[self.var] - except KeyError: - # The variable reference substitutes to nothing. - return '' + def _generate(self, target, source, env, for_signature): + c = env.get(self.var, '') + gen_cmd = apply(Action, (c,), self.gen_kw) + if not gen_cmd: + raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) + return gen_cmd - def __cmp__(self, other): - return cmp(self.__dict__, other) + def __call__(self, target, source, env, *args, **kw): + args = (self, target, source, env) + args + c = self.get_parent_class(env) + return apply(c.__call__, args, kw) + + def get_contents(self, target, source, env, dict=None): + c = self.get_parent_class(env) + return c.get_contents(self, target, source, env, dict) class FunctionAction(_ActionAction): """Class for Python function actions.""" diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 74812df4..f52496a2 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -298,12 +298,10 @@ class ActionTestCase(unittest.TestCase): pass a1 = SCons.Action.Action("$FOO") - assert isinstance(a1, SCons.Action.CommandGeneratorAction), a1 - assert isinstance(a1.generator, SCons.Action.LazyCmdGenerator), a1.generator + assert isinstance(a1, SCons.Action.LazyAction), a1 a2 = SCons.Action.Action("$FOO", strfunction=foo) - assert isinstance(a2, SCons.Action.CommandGeneratorAction), a2 - assert isinstance(a2.generator, SCons.Action.LazyCmdGenerator), a2.generator + assert isinstance(a2, SCons.Action.LazyAction), a2 def test_no_action(self): """Test when the Action() factory can't create an action object @@ -1468,16 +1466,16 @@ class LazyActionTestCase(unittest.TestCase): def test___init__(self): """Test creation of a lazy-evaluation Action """ - # Environment variable references should create a special - # type of CommandGeneratorAction that lazily evaluates the - # variable. + # Environment variable references should create a special type + # of LazyAction that lazily evaluates the variable for whether + # it's a string or something else before doing anything. a9 = SCons.Action.Action('$FOO') - assert isinstance(a9, SCons.Action.CommandGeneratorAction), a9 - assert a9.generator.var == 'FOO', a9.generator.var + assert isinstance(a9, SCons.Action.LazyAction), a9 + assert a9.var == 'FOO', a9.var a10 = SCons.Action.Action('${FOO}') - assert isinstance(a9, SCons.Action.CommandGeneratorAction), a10 - assert a10.generator.var == 'FOO', a10.generator.var + assert isinstance(a10, SCons.Action.LazyAction), a10 + assert a10.var == 'FOO', a10.var def test_genstring(self): """Test the lazy-evaluation Action genstring() method @@ -1487,6 +1485,8 @@ class LazyActionTestCase(unittest.TestCase): a = SCons.Action.Action('$BAR') s = a.genstring([], [], env=Environment(BAR=f, s=self)) assert s == "f(target, source, env)", s + s = a.genstring([], [], env=Environment(BAR='xxx', s=self)) + assert s == 'xxx', s def test_execute(self): """Test executing a lazy-evaluation Action @@ -1498,6 +1498,10 @@ class LazyActionTestCase(unittest.TestCase): a = SCons.Action.Action('$BAR') a([], [], env=Environment(BAR = f, s = self)) assert self.test == 1, self.test + cmd = r'%s %s %s lazy' % (python, act_py, outfile) + a([], [], env=Environment(BAR = cmd, s = self)) + c = test.read(outfile, 'r') + assert c == "act.py: 'lazy'\n", c def test_get_contents(self): """Test fetching the contents of a lazy-evaluation Action diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 4c9a0be1..71383e7a 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -1756,7 +1756,7 @@ f5: \ a = env.Action('$FOO') assert a, a - assert a.__class__ is SCons.Action.CommandGeneratorAction, a + assert a.__class__ is SCons.Action.LazyAction, a a = env.Action(['$FOO', 'foo']) assert a, a