diff options
author | Steven Knight <knight@baldmt.com> | 2004-06-25 04:10:24 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2004-06-25 04:10:24 (GMT) |
commit | c2bb425dcb2907f50a485469b69e83884fed6fb4 (patch) | |
tree | eec003c4e6e332651cf70c8896612b17b7acb290 /src | |
parent | 5f1ca10deda557947d8669098fdce1852b38b81f (diff) | |
download | SCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.zip SCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.tar.gz SCons-c2bb425dcb2907f50a485469b69e83884fed6fb4.tar.bz2 |
Officially support target_factory and source_factory when creating a Builder.
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 4 | ||||
-rw-r--r-- | src/engine/SCons/Action.py | 22 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 36 | ||||
-rw-r--r-- | src/engine/SCons/Builder.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Environment.py | 5 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 48 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 82 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 8 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 2 |
9 files changed, 130 insertions, 79 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index ee75857..f2ee12d 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -138,6 +138,10 @@ RELEASE 0.96 - XXX - Slim down the internal Sig.Calculator class by eliminating methods whose functionality is now covered by Node methods. + - Document use of the target_factory and source_factory keyword + arguments when creating Builder objects. Enhance Dir Nodes so that + they can be created with user-specified Builder objects. + From Gary Oberbrunner: - Add a --debug=presub option to print actions prior to substitution. diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index c8d62cd..6a90c76 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -191,25 +191,29 @@ def _do_create_action(act, *args, **kw): return apply(ListAction, (listCmdActions,)+args, kw) return None -def Action(act, strfunction=_null, varlist=[]): +def Action(act, strfunction=_null, varlist=[], presub=_null): """A factory for action objects.""" if SCons.Util.is_List(act): - acts = map(lambda x, s=strfunction, v=varlist: - _do_create_action(x, strfunction=s, varlist=v), + acts = map(lambda x, s=strfunction, v=varlist, ps=presub: + _do_create_action(x, strfunction=s, varlist=v, presub=ps), act) acts = filter(lambda x: not x is None, acts) if len(acts) == 1: return acts[0] else: - return ListAction(acts, strfunction=strfunction, varlist=varlist) + return ListAction(acts, strfunction=strfunction, varlist=varlist, presub=presub) else: - return _do_create_action(act, strfunction=strfunction, varlist=varlist) + return _do_create_action(act, strfunction=strfunction, varlist=varlist, presub=presub) class ActionBase: """Base class for actions that create output objects.""" - def __init__(self, strfunction=_null, **kw): + def __init__(self, strfunction=_null, presub=_null, **kw): if not strfunction is _null: self.strfunction = strfunction + if presub is _null: + self.presub = print_actions_presub + else: + self.presub = presub def __cmp__(self, other): return cmp(self.__dict__, other.__dict__) @@ -223,12 +227,12 @@ class ActionBase: target = [target] if not SCons.Util.is_List(source): source = [source] - if presub is _null: presub = print_actions_presub + if presub is _null: presub = self.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 ') + l = string.join(self.presub_lines(env), '\n ') out = "Building %s with action(s):\n %s\n" % (t, l) sys.stdout.write(out) if show and self.strfunction: @@ -243,7 +247,7 @@ class ActionBase: else: return 0 - def presub(self, env): + def presub_lines(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 diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index f699e61..9a89623 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -392,11 +392,27 @@ class ActionBaseTestCase(unittest.TestCase): result = a("out", "in", env) assert result == 0, result s = sio.getvalue() + assert s == 'execfunc("out", "in")\n', s + + sio = StringIO.StringIO() + sys.stdout = sio + result = a("out", "in", env, presub=1) + assert result == 0, result + s = sio.getvalue() + assert s == 'Building out with action(s):\n execfunc(env, target, source)\nexecfunc("out", "in")\n', s + + a2 = SCons.Action.Action(execfunc) + + sio = StringIO.StringIO() + sys.stdout = sio + result = a2("out", "in", env) + assert result == 0, result + s = sio.getvalue() assert s == 'Building out with action(s):\n execfunc(env, target, source)\nexecfunc("out", "in")\n', s sio = StringIO.StringIO() sys.stdout = sio - result = a("out", "in", env, presub=0) + result = a2("out", "in", env, presub=0) assert result == 0, result s = sio.getvalue() assert s == 'execfunc("out", "in")\n', s @@ -428,36 +444,36 @@ class ActionBaseTestCase(unittest.TestCase): SCons.Action.print_actions_presub = save_print_actions_presub SCons.Action.execute_actions = save_execute_actions - def test_presub(self): - """Test the presub() method + def test_presub_lines(self): + """Test the presub_lines() method """ env = Environment() a = SCons.Action.Action("x") - s = a.presub(env) + s = a.presub_lines(env) assert s == ['x'], s a = SCons.Action.Action(["y", "z"]) - s = a.presub(env) + s = a.presub_lines(env) assert s == ['y', 'z'], s def func(): pass a = SCons.Action.Action(func) - s = a.presub(env) + s = a.presub_lines(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) + s = a.presub_lines(env) assert s == ["generator"], s - s = a.presub(Environment(GEN = 'ed')) + s = a.presub_lines(Environment(GEN = 'ed')) assert s == ["generated"], s a = SCons.Action.Action("$ACT") - s = a.presub(env) + s = a.presub_lines(env) assert s == [''], s - s = a.presub(Environment(ACT = 'expanded action')) + s = a.presub_lines(Environment(ACT = 'expanded action')) assert s == ['expanded action'], s def test_get_actions(self): diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 733a26a..1dcf84c 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -285,7 +285,7 @@ def _init_nodes(builder, env, overrides, tlist, slist): if t.side_effect: raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) if t.has_builder(): - if not t.env is env: + if not t.env is None and not t.env is env: t_contents = t.builder.action.get_contents(tlist, slist, t.env) contents = t.builder.action.get_contents(tlist, slist, env) diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 5766523..080232a 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -1172,10 +1172,7 @@ class Base: targets = self.arg2nodes(target, self.fs.Entry) for side_effect in side_effects: - # A builder of 1 means the node is supposed to appear - # buildable without actually having a builder, so we allow - # it to be a side effect as well. - if side_effect.has_builder() and side_effect.builder != 1: + if side_effect.multiple_side_effect_has_builder(): raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) side_effect.add_source(targets) side_effect.side_effect = 1 diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 2f115c9..52a75ce 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -176,10 +176,24 @@ Unlink = SCons.Action.Action(UnlinkFunc, None) def MkdirFunc(target, source, env): t = target[0] - t.fs.mkdir(t.path) + if not t.fs.exists(t.path): + t.fs.mkdir(t.path) return 0 -Mkdir = SCons.Action.Action(MkdirFunc, None) +Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) + +MkdirBuilder = None + +def get_MkdirBuilder(): + global MkdirBuilder + if MkdirBuilder is None: + import SCons.Builder + import SCons.Defaults + env = SCons.Defaults.DefaultEnvironment() + MkdirBuilder = SCons.Builder.Builder(action = Mkdir, + env = env, + explain = None) + return MkdirBuilder def CacheRetrieveFunc(target, source, env): t = target[0] @@ -1118,7 +1132,7 @@ class Dir(Base): self.entries['.'] = self self.entries['..'] = self.dir self.cwd = self - self.builder = 1 + self.builder = get_MkdirBuilder() self.searched = 0 self._sconsign = None self.build_dirs = [] @@ -1238,7 +1252,13 @@ class Dir(Base): def build(self, **kw): """A null "builder" for directories.""" - pass + global MkdirBuilder + if not self.builder is MkdirBuilder: + apply(SCons.Node.Node.build, [self,], kw) + + def multiple_side_effect_has_builder(self): + global MkdirBuilder + return not self.builder is MkdirBuilder and self.has_builder() def alter_targets(self): """Return any corresponding targets in a build directory. @@ -1262,6 +1282,8 @@ class Dir(Base): def current(self, calc=None): """If all of our children were up-to-date, then this directory was up-to-date, too.""" + if not self.builder is MkdirBuilder and not self.exists(): + return 0 state = 0 for kid in self.children(): s = kid.get_state() @@ -1299,16 +1321,6 @@ class Dir(Base): return self.srcdir return Base.srcnode(self) - def get_executor(self, create=1): - """Fetch the action executor for this node. Create one if - there isn't already one, and requested to do so.""" - try: - executor = self.executor - except AttributeError: - executor = DummyExecutor() - self.executor = executor - return executor - def get_timestamp(self): """Return the latest timestamp from among our children""" stamp = 0 @@ -1482,8 +1494,12 @@ class File(Base): listDirs.reverse() for dirnode in listDirs: try: - Mkdir(dirnode, [], None) - # The Mkdir() action may or may not have actually + # Don't call dirnode.build(), call the base Node method + # directly because we definitely *must* create this + # directory. The dirnode.build() method will suppress + # the build if it's the default builder. + SCons.Node.Node.build(dirnode) + # The build() action may or may not have actually # created the directory, depending on whether the -n # option was used or not. Delete the _exists and # _rexists attributes so they can be reevaluated. diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index d4137c1..bbc64ef 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -281,8 +281,33 @@ class BuildDirTestCase(unittest.TestCase): f8.rfile().path # Verify the Mkdir and Link actions are called + d9 = fs.Dir('build/var2/new_dir') f9 = fs.File('build/var2/new_dir/test9.out') + class MkdirAction(Action): + def __init__(self, dir_made): + self.dir_made = dir_made + def __call__(self, target, source, env, errfunc): + self.dir_made.extend(target) + + save_Link = SCons.Node.FS.Link + link_made = [] + def link_func(target, source, env, link_made=link_made): + link_made.append(target) + SCons.Node.FS.Link = link_func + + try: + dir_made = [] + d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made)) + f9.exists() + expect = os.path.join('build', 'var2', 'new_dir') + assert dir_made[0].path == expect, dir_made[0].path + expect = os.path.join('build', 'var2', 'new_dir', 'test9.out') + assert link_made[0].path == expect, link_made[0].path + assert f9.linked + finally: + SCons.Node.FS.Link = save_Link + # Test for an interesting pathological case...we have a source # file in a build path, but not in a source path. This can # happen if you switch from duplicate=1 to duplicate=0, then @@ -312,29 +337,6 @@ class BuildDirTestCase(unittest.TestCase): var2_new_dir = os.path.normpath('build/var2/new_dir') assert bdt == [var1_new_dir, var2_new_dir], bdt - save_Mkdir = SCons.Node.FS.Mkdir - dir_made = [] - def mkdir_func(target, source, env, dir_made=dir_made): - dir_made.append(target) - SCons.Node.FS.Mkdir = mkdir_func - - save_Link = SCons.Node.FS.Link - link_made = [] - def link_func(target, source, env, link_made=link_made): - link_made.append(target) - SCons.Node.FS.Link = link_func - - try: - f9.exists() - expect = os.path.join('build', 'var2', 'new_dir') - assert dir_made[0].path == expect, dir_made[0].path - expect = os.path.join('build', 'var2', 'new_dir', 'test9.out') - assert link_made[0].path == expect, link_made[0].path - assert f9.linked - finally: - SCons.Node.FS.Mkdir = save_Mkdir - SCons.Node.FS.Link = save_Link - # Test that an IOError trying to Link a src file # into a BuildDir ends up throwing a StopError. fIO = fs.File("build/var2/IOError") @@ -754,7 +756,7 @@ class FSTestCase(unittest.TestCase): d1.builder_set(Builder(fs.File)) d1.env_set(Environment()) d1.build() - assert not built_it + assert built_it built_it = None assert not built_it @@ -1497,22 +1499,24 @@ class prepareTestCase(unittest.TestCase): exc_caught = 1 assert exc_caught, "Should have caught a StopError." - save_Mkdir = SCons.Node.FS.Mkdir - dir_made = [] - def mkdir_func(target, source, env, dir_made=dir_made): - dir_made.append(target) - SCons.Node.FS.Mkdir = mkdir_func + class MkdirAction(Action): + def __init__(self, dir_made): + self.dir_made = dir_made + def __call__(self, target, source, env, errfunc): + self.dir_made.extend(target) - file = fs.File(os.path.join("new_dir", "xyz")) - try: - file.set_state(SCons.Node.up_to_date) - file.prepare() - assert dir_made == [], dir_made - file.set_state(0) - file.prepare() - assert dir_made[0].path == "new_dir", dir_made[0].path - finally: - SCons.Node.FS.Mkdir = save_Mkdir + dir_made = [] + new_dir = fs.Dir("new_dir") + new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made)) + xyz = fs.File(os.path.join("new_dir", "xyz")) + + xyz.set_state(SCons.Node.up_to_date) + xyz.prepare() + assert dir_made == [], dir_made + xyz.set_state(0) + xyz.prepare() + print "dir_made[0] =", dir_made[0] + assert dir_made[0].path == "new_dir", dir_made[0] dir = fs.Dir("dir") dir.prepare() diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index cd7aa18..72ddd74 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -304,6 +304,14 @@ class NodeTestCase(unittest.TestCase): n1.builder_set(Builder()) assert n1.has_builder() == 1 + def test_multiple_side_effect_has_builder(self): + """Test the multiple_side_effect_has_builder() method + """ + n1 = SCons.Node.Node() + assert n1.multiple_side_effect_has_builder() == 0 + n1.builder_set(Builder()) + assert n1.multiple_side_effect_has_builder() == 1 + def test_is_derived(self): """Test the is_derived() method """ diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 51e6628..603762e 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -299,6 +299,8 @@ class Node: b = self.builder return not b is None + multiple_side_effect_has_builder = has_builder + def is_derived(self): """ Returns true iff this node is derived (i.e. built). |