From 09cad6ed1ffb436742adbbf6608bb094b4a236e2 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Sat, 13 Nov 2004 21:56:57 +0000 Subject: Allow Alias Nodes to have Actions. --- doc/man/scons.1 | 15 ++- src/CHANGES.txt | 3 + src/engine/SCons/Environment.py | 72 +++++++++----- src/engine/SCons/EnvironmentTests.py | 19 ++++ src/engine/SCons/Node/Alias.py | 45 ++++++--- src/engine/SCons/Node/AliasTests.py | 14 +-- src/engine/SCons/Node/NodeTests.py | 13 +++ src/engine/SCons/Node/__init__.py | 7 ++ test/Alias-scanner.py | 61 ------------ test/Alias.py | 181 ----------------------------------- test/Alias/Alias.py | 169 ++++++++++++++++++++++++++++++++ test/Alias/action.py | 138 ++++++++++++++++++++++++++ test/Alias/errors.py | 46 +++++++++ test/Alias/scanner.py | 61 ++++++++++++ 14 files changed, 553 insertions(+), 291 deletions(-) delete mode 100644 test/Alias-scanner.py delete mode 100644 test/Alias.py create mode 100644 test/Alias/Alias.py create mode 100644 test/Alias/action.py create mode 100644 test/Alias/errors.py create mode 100644 test/Alias/scanner.py diff --git a/doc/man/scons.1 b/doc/man/scons.1 index fdfae69..59e0d62 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -2266,11 +2266,17 @@ can be converted into an Action object '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI Alias( alias ", [" targets ]) +.RI Alias( alias ", [" targets ", [" action ]]) .TP -.RI env.Alias( alias ", [" targets ]) +.RI env.Alias( alias ", [" targets ", [" action ]]) Creates one or more phony targets that expand to one or more other targets. +An optional +.I action +(command) +or list of actions +can be specified that will be executed +whenever the any of the alias targets are out-of-date. Returns the Node object representing the alias, which exists outside of any file system. This Node object, or the alias name, @@ -2278,7 +2284,8 @@ may be used as a dependency of any other target, including another alias. .B Alias can be called multiple times for the same -alias to add additional targets to the alias. +alias to add additional targets to the alias, +or additional actions to the list for this alias. .ES Alias('install') @@ -2287,6 +2294,8 @@ Alias(['install', 'install-lib'], '/usr/local/lib') env.Alias('install', ['/usr/local/bin', '/usr/local/lib']) env.Alias('install', ['/usr/local/man']) + +env.Alias('update', ['file1', 'file2'], "update_database $SOURCES") .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/CHANGES.txt b/src/CHANGES.txt index abfbd65..003163b 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -124,6 +124,9 @@ RELEASE 0.97 - XXX to extend() list, so Python 1.5.2 doesn't get pretty-printing of Node lists, but everything should still function.) + - Allow Aliases to have actions that will be executed whenever + any of the expanded Alias targets are out of date. + From Wayne Lee: - Avoid "maximum recursion limit" errors when removing $(-$) pairs diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 443c6b0..ef3b2d0 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -96,6 +96,7 @@ AliasBuilder = SCons.Builder.Builder(action = alias_builder, target_factory = SCons.Node.Alias.default_ans.Alias, source_factory = SCons.Node.FS.default_fs.Entry, multi = 1, + is_explicit = None, name='AliasBuilder') def our_deepcopy(x): @@ -1036,29 +1037,56 @@ class Base: n.add_post_action(action) return nodes - def Alias(self, target, *source, **kw): - if not SCons.Util.is_List(target): - target = [target] - tlist = [] - for t in target: - if not isinstance(t, SCons.Node.Alias.Alias): - t = self.arg2nodes(self.subst(t), self.ans.Alias)[0] - tlist.append(t) - try: - s = kw['source'] - except KeyError: - try: - s = source[0] - except IndexError: - s = None - if s: - if not SCons.Util.is_List(s): - s = [s] - s = filter(None, s) - s = self.arg2nodes(s, self.fs.Entry) + def Alias(self, target, source=[], action=None, **kw): + tlist = self.arg2nodes(target, self.ans.Alias) + if not SCons.Util.is_List(source): + source = [source] + source = filter(None, source) + + if not action: + if not source: + # There are no source files and no action, so just + # return a target list of classic Alias Nodes, without + # any builder. The externally visible effect is that + # this will make the wrapping Script.BuildTask class + # say that there's "Nothing to be done" for this Alias, + # instead of that it's "up to date." + return tlist + + # No action, but there are sources. Re-call all the target + # builders to add the sources to each target. + result = [] for t in tlist: - AliasBuilder(self, t, s) - return tlist + bld = t.get_builder(AliasBuilder) + result.extend(bld(self, t, source)) + return result + + action = SCons.Action.Action(action) + nkw = self.subst_kw(kw) + nkw['source_factory'] = self.fs.Entry + nkw['multi'] = 1 + nkw['action'] = action + bld = apply(SCons.Builder.Builder, (), nkw) + + # Apply the Builder separately to each target so that the Aliases + # stay separate. If we did one "normal" Builder call with the + # whole target list, then all of the target Aliases would be + # associated under a single Executor. + result = [] + for t in tlist: + # Calling the convert() method will cause a new Executor to be + # created from scratch, so we have to explicitly initialize + # it with the target's existing sources, plus our new ones, + # so nothing gets lost. + b = t.get_builder() + if b is None or b is AliasBuilder: + b = bld + else: + nkw['action'] = b.action + action + b = apply(SCons.Builder.Builder, (), nkw) + t.convert() + result.extend(b(self, t, t.sources + source)) + return result def AlwaysBuild(self, *targets): tlist = [] diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 0a0f260..9b84f3c 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -1797,6 +1797,7 @@ f5: \ tgt = env.Alias('new_alias')[0] assert str(tgt) == 'new_alias', tgt assert tgt.sources == [], tgt.sources + assert not hasattr(tgt, 'builder'), tgt.builder tgt = env.Alias('None_alias', None)[0] assert str(tgt) == 'None_alias', tgt @@ -1833,6 +1834,24 @@ f5: \ assert str(t2.sources[0]) == 'asrc6', map(str, t2.sources) assert str(t2.sources[1]) == 'asrc7', map(str, t2.sources) + tgt = env.Alias('add', 's1') + tgt = env.Alias('add', 's2')[0] + s = map(str, tgt.sources) + assert s == ['s1', 's2'], s + tgt = env.Alias(tgt, 's3')[0] + s = map(str, tgt.sources) + assert s == ['s1', 's2', 's3'], s + + tgt = env.Alias('act', None, "action1")[0] + s = str(tgt.builder.action) + assert s == "action1", s + tgt = env.Alias('act', None, "action2")[0] + s = str(tgt.builder.action) + assert s == "action1\naction2", s + tgt = env.Alias(tgt, None, "action3")[0] + s = str(tgt.builder.action) + assert s == "action1\naction2\naction3", s + def test_AlwaysBuild(self): """Test the AlwaysBuild() method""" env = Environment(FOO='fff', BAR='bbb') diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py index 89127a3..c361838 100644 --- a/src/engine/SCons/Node/Alias.py +++ b/src/engine/SCons/Node/Alias.py @@ -39,11 +39,15 @@ import SCons.Node import SCons.Util class AliasNameSpace(UserDict.UserDict): - def Alias(self, name): - if self.has_key(name): - raise SCons.Errors.UserError - self[name] = SCons.Node.Alias.Alias(name) - return self[name] + def Alias(self, name, **kw): + if isinstance(name, SCons.Node.Alias.Alias): + return name + try: + a = self[name] + except KeyError: + a = apply(SCons.Node.Alias.Alias, (name,), kw) + self[name] = a + return a def lookup(self, name): try: @@ -59,18 +63,11 @@ class Alias(SCons.Node.Node): def __str__(self): return self.name - def build(self): - """A "builder" for aliases.""" - pass - + really_build = SCons.Node.Node.build current = SCons.Node.Node.children_are_up_to_date - def sconsign(self): - """An Alias is not recorded in .sconsign files""" - pass - def is_under(self, dir): - # Make Alias nodes get built regardless of + # Make Alias nodes get built regardless of # what directory scons was run from. Alias nodes # are outside the filesystem: return 1 @@ -82,7 +79,25 @@ class Alias(SCons.Node.Node): for kid in self.children(None): contents = contents + kid.get_contents() return contents - + + def sconsign(self): + """An Alias is not recorded in .sconsign files""" + pass + + # + # + # + + def build(self): + """A "builder" for aliases.""" + pass + + def convert(self): + try: del self.builder + except AttributeError: pass + self.reset_executor() + self.build = self.really_build + default_ans = AliasNameSpace() SCons.Node.arg2nodes_lookups.append(default_ans.lookup) diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py index afae46b..417cc2b 100644 --- a/src/engine/SCons/Node/AliasTests.py +++ b/src/engine/SCons/Node/AliasTests.py @@ -42,15 +42,11 @@ class AliasTestCase(unittest.TestCase): """ ans = SCons.Node.Alias.AliasNameSpace() - a = ans.Alias('a1') - assert a.name == 'a1', a.name - - try: - ans.Alias('a1') - except SCons.Errors.UserError: - pass - else: - raise TestFailed, "did not catch expected UserError" + a1 = ans.Alias('a1') + assert a1.name == 'a1', a1.name + + a2 = ans.Alias('a1') + assert a1 is a2, (a1, a2) def test_lookup(self): """Test the lookup() method diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 657ac9b..e0956e1 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -390,6 +390,19 @@ class NodeTestCase(unittest.TestCase): n1.builder_set(Builder(is_explicit=None)) assert not n1.has_explicit_builder() + def test_get_builder(self): + """Test the get_builder() method""" + n1 = SCons.Node.Node() + b = n1.get_builder() + assert b is None, b + b = n1.get_builder(777) + assert b == 777, b + n1.builder_set(888) + b = n1.get_builder() + assert b == 888, b + b = n1.get_builder(999) + assert b == 888, b + def test_multiple_side_effect_has_builder(self): """Test the multiple_side_effect_has_builder() method """ diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 36b0aed..fc3d7ec 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -329,6 +329,13 @@ class Node: directories).""" return self.has_builder() and self.builder.is_explicit + def get_builder(self, default_builder=None): + """Return the set builder, or a specified default value""" + try: + return self.builder + except AttributeError: + return default_builder + multiple_side_effect_has_builder = has_builder def is_derived(self): diff --git a/test/Alias-scanner.py b/test/Alias-scanner.py deleted file mode 100644 index 3ce14dd..0000000 --- a/test/Alias-scanner.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/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. -# - -""" -Test that an Alias of a node with a Scanner works. -""" - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import sys -import TestSCons -import TestCmd - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -def cat(env, source, target): - target = str(target[0]) - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() - -XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') -env = Environment() -env.Append(BUILDERS = { 'XBuilder': XBuilder }) -f = env.XBuilder(source = ['file.x'], target = ['file.c']) -env.Alias(target = ['cfiles'], source = f) -Default(['cfiles']) -""") - -test.write('file.x', "file.x\n") - -test.run() - -test.fail_test(test.read('file.c') != "file.x\n") - -test.pass_test() diff --git a/test/Alias.py b/test/Alias.py deleted file mode 100644 index 1facd28..0000000 --- a/test/Alias.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/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__" - -import os -import sys -import TestSCons -import TestCmd - -python = TestSCons.python - -test = TestSCons.TestSCons(match=TestCmd.match_re) - -test.subdir('sub1', 'sub2') - -test.write('build.py', r""" -import sys -open(sys.argv[1], 'wb').write(open(sys.argv[2], 'rb').read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -B = Builder(action = r"%s build.py $TARGET $SOURCES") -env = Environment() -env['BUILDERS']['B'] = B -env.B(target = 'f1.out', source = 'f1.in') -env.B(target = 'f2.out', source = 'f2.in') -env.B(target = 'f3.out', source = 'f3.in') -SConscript('sub1/SConscript', "env") -SConscript('sub2/SConscript', "env") - -foo = Alias('foo') -foo2 = env.Alias('foo', ['f2.out', 'sub1']) -assert foo == foo2 -bar = Alias('bar', ['sub2', 'f3.out']) -env.Alias('blat', ['sub2', 'f3.out']) -env.Alias('blat', ['f2.out', 'sub1']) -env.Depends('f1.out', 'bar') - -assert Alias('foo') == foo -assert Alias('bar') == bar - -""" % python) - -test.write(['sub1', 'SConscript'], """ -Import("env") -env.B(target = 'f4.out', source = 'f4.in') -env.B(target = 'f5.out', source = 'f5.in') -env.B(target = 'f6.out', source = 'f6.in') -""") - -test.write(['sub2', 'SConscript'], """ -Import("env") -env.B(target = 'f7.out', source = 'f7.in') -env.B(target = 'f8.out', source = 'f8.in') -env.B(target = 'f9.out', source = 'f9.in') -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") -test.write('f3.in', "f3.in\n") - -test.write(['sub1', 'f4.in'], "sub1/f4.in\n") -test.write(['sub1', 'f5.in'], "sub1/f5.in\n") -test.write(['sub1', 'f6.in'], "sub1/f6.in\n") - -test.write(['sub2', 'f7.in'], "sub2/f7.in\n") -test.write(['sub2', 'f8.in'], "sub2/f8.in\n") -test.write(['sub2', 'f9.in'], "sub2/f9.in\n") - -test.run(arguments = 'foo') - -test.fail_test(os.path.exists(test.workpath('f1.out'))) -test.fail_test(not os.path.exists(test.workpath('f2.out'))) -test.fail_test(os.path.exists(test.workpath('f3.out'))) - -test.fail_test(not os.path.exists(test.workpath('sub1', 'f4.out'))) -test.fail_test(not os.path.exists(test.workpath('sub1', 'f5.out'))) -test.fail_test(not os.path.exists(test.workpath('sub1', 'f6.out'))) - -test.fail_test(os.path.exists(test.workpath('sub2', 'f7.out'))) -test.fail_test(os.path.exists(test.workpath('sub2', 'f8.out'))) -test.fail_test(os.path.exists(test.workpath('sub2', 'f9.out'))) - -test.up_to_date(arguments = 'foo') - -test.run(arguments = 'f1.out') - -test.fail_test(not os.path.exists(test.workpath('f1.out'))) -test.fail_test(not os.path.exists(test.workpath('f3.out'))) - -test.fail_test(not os.path.exists(test.workpath('sub2', 'f7.out'))) -test.fail_test(not os.path.exists(test.workpath('sub2', 'f8.out'))) -test.fail_test(not os.path.exists(test.workpath('sub2', 'f9.out'))) - -test.up_to_date(arguments = 'f1.out') - -os.unlink(test.workpath('f2.out')) -os.unlink(test.workpath('f3.out')) - -test.run(arguments = 'blat') - -test.fail_test(not os.path.exists(test.workpath('f2.out'))) -test.fail_test(not os.path.exists(test.workpath('f3.out'))) - -test.write('f3.in', "f3.in 2 \n") - -test.run(arguments = 'f1.out', - stdout = test.wrap_stdout(".* build.py f3.out f3.in\n.* build.py f1.out f1.in\n")) - -test.up_to_date(arguments = 'f1.out') - -test.write('SConstruct', """ -TargetSignatures('content') -B = Builder(action = r"%s build.py $TARGET $SOURCES") -env = Environment() -env['BUILDERS']['B'] = B -env.B(target = 'f1.out', source = 'f1.in') -env.B(target = 'f2.out', source = 'f2.in') -env.B(target = 'f3.out', source = 'f3.in') -SConscript('sub1/SConscript', "env") -SConscript('sub2/SConscript', "env") -env.Alias('foo', ['f2.out', 'sub1']) -env.Alias('bar', ['sub2', 'f3.out']) -env.Alias('blat', ['sub2', 'f3.out']) -env.Alias('blat', ['f2.out', 'sub1']) -env.Depends('f1.out', 'bar') -""" % python) - -os.unlink(test.workpath('f1.out')) - -test.run(arguments = 'f1.out') - -test.fail_test(not os.path.exists(test.workpath('f1.out'))) - -test.write('f3.in', "f3.in 3 \n") - -test.run(arguments = 'f1.out', - match = TestCmd.match_exact, - stdout = test.wrap_stdout("""\ -%s build.py f3.out f3.in -%s build.py f1.out f1.in -""" % (python, python))) - -test.up_to_date(arguments = 'f1.out') - -test.write('SConstruct', """ -env=Environment() -TargetSignatures('content') -env.Alias('C', 'D') -env.Alias('B', 'C') -env.Alias('A', 'B') -""") - -test.run(arguments='A', - stderr="scons: \\*\\*\\* Source `D' not found, needed by target `C'. Stop.\n", - status=2) - -test.pass_test() diff --git a/test/Alias/Alias.py b/test/Alias/Alias.py new file mode 100644 index 0000000..56efafd --- /dev/null +++ b/test/Alias/Alias.py @@ -0,0 +1,169 @@ +#!/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__" + +import os +import sys +import TestSCons +import TestCmd + +python = TestSCons.python + +test = TestSCons.TestSCons(match=TestCmd.match_re) + +test.subdir('sub1', 'sub2') + +test.write('build.py', r""" +import sys +open(sys.argv[1], 'wb').write(open(sys.argv[2], 'rb').read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +B = Builder(action = r"%s build.py $TARGET $SOURCES") +env = Environment() +env['BUILDERS']['B'] = B +env.B(target = 'f1.out', source = 'f1.in') +env.B(target = 'f2.out', source = 'f2.in') +env.B(target = 'f3.out', source = 'f3.in') +SConscript('sub1/SConscript', "env") +SConscript('sub2/SConscript', "env") + +foo = Alias('foo') +foo2 = env.Alias('foo', ['f2.out', 'sub1']) +assert foo == foo2 +bar = Alias('bar', ['sub2', 'f3.out']) +env.Alias('blat', ['sub2', 'f3.out']) +env.Alias('blat', ['f2.out', 'sub1']) +env.Depends('f1.out', 'bar') + +assert Alias('foo') == foo +assert Alias('bar') == bar + +""" % python) + +test.write(['sub1', 'SConscript'], """ +Import("env") +env.B(target = 'f4.out', source = 'f4.in') +env.B(target = 'f5.out', source = 'f5.in') +env.B(target = 'f6.out', source = 'f6.in') +""") + +test.write(['sub2', 'SConscript'], """ +Import("env") +env.B(target = 'f7.out', source = 'f7.in') +env.B(target = 'f8.out', source = 'f8.in') +env.B(target = 'f9.out', source = 'f9.in') +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") + +test.write(['sub1', 'f4.in'], "sub1/f4.in\n") +test.write(['sub1', 'f5.in'], "sub1/f5.in\n") +test.write(['sub1', 'f6.in'], "sub1/f6.in\n") + +test.write(['sub2', 'f7.in'], "sub2/f7.in\n") +test.write(['sub2', 'f8.in'], "sub2/f8.in\n") +test.write(['sub2', 'f9.in'], "sub2/f9.in\n") + +test.run(arguments = 'foo') + +test.fail_test(os.path.exists(test.workpath('f1.out'))) +test.fail_test(not os.path.exists(test.workpath('f2.out'))) +test.fail_test(os.path.exists(test.workpath('f3.out'))) + +test.fail_test(not os.path.exists(test.workpath('sub1', 'f4.out'))) +test.fail_test(not os.path.exists(test.workpath('sub1', 'f5.out'))) +test.fail_test(not os.path.exists(test.workpath('sub1', 'f6.out'))) + +test.fail_test(os.path.exists(test.workpath('sub2', 'f7.out'))) +test.fail_test(os.path.exists(test.workpath('sub2', 'f8.out'))) +test.fail_test(os.path.exists(test.workpath('sub2', 'f9.out'))) + +test.up_to_date(arguments = 'foo') + +test.run(arguments = 'f1.out') + +test.fail_test(not os.path.exists(test.workpath('f1.out'))) +test.fail_test(not os.path.exists(test.workpath('f3.out'))) + +test.fail_test(not os.path.exists(test.workpath('sub2', 'f7.out'))) +test.fail_test(not os.path.exists(test.workpath('sub2', 'f8.out'))) +test.fail_test(not os.path.exists(test.workpath('sub2', 'f9.out'))) + +test.up_to_date(arguments = 'f1.out') + +os.unlink(test.workpath('f2.out')) +os.unlink(test.workpath('f3.out')) + +test.run(arguments = 'blat') + +test.fail_test(not os.path.exists(test.workpath('f2.out'))) +test.fail_test(not os.path.exists(test.workpath('f3.out'))) + +test.write('f3.in', "f3.in 2 \n") + +test.run(arguments = 'f1.out', + stdout = test.wrap_stdout(".* build.py f3.out f3.in\n.* build.py f1.out f1.in\n")) + +test.up_to_date(arguments = 'f1.out') + +test.write('SConstruct', """ +TargetSignatures('content') +B = Builder(action = r"%s build.py $TARGET $SOURCES") +env = Environment() +env['BUILDERS']['B'] = B +env.B(target = 'f1.out', source = 'f1.in') +env.B(target = 'f2.out', source = 'f2.in') +env.B(target = 'f3.out', source = 'f3.in') +SConscript('sub1/SConscript', "env") +SConscript('sub2/SConscript', "env") +env.Alias('foo', ['f2.out', 'sub1']) +env.Alias('bar', ['sub2', 'f3.out']) +env.Alias('blat', ['sub2', 'f3.out']) +env.Alias('blat', ['f2.out', 'sub1']) +env.Depends('f1.out', 'bar') +""" % python) + +os.unlink(test.workpath('f1.out')) + +test.run(arguments = 'f1.out') + +test.fail_test(not os.path.exists(test.workpath('f1.out'))) + +test.write('f3.in', "f3.in 3 \n") + +test.run(arguments = 'f1.out', + match = TestCmd.match_exact, + stdout = test.wrap_stdout("""\ +%s build.py f3.out f3.in +%s build.py f1.out f1.in +""" % (python, python))) + +test.up_to_date(arguments = 'f1.out') + +test.pass_test() diff --git a/test/Alias/action.py b/test/Alias/action.py new file mode 100644 index 0000000..ea0eac2 --- /dev/null +++ b/test/Alias/action.py @@ -0,0 +1,138 @@ +#!/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. +# + +""" +Test that Aliases with actions work. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import sys +import TestSCons +import TestCmd + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +def cat(target, source, env): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() + +def foo(target, source, env): + target = map(str, target) + source = map(str, source) + open('foo', 'wb').write("foo(%s, %s)\\n" % (target, source)) + +def bar(target, source, env): + target = map(str, target) + source = map(str, source) + open('bar', 'wb').write("bar(%s, %s)\\n" % (target, source)) + +env = Environment(BUILDERS = {'Cat':Builder(action=cat)}) +env.Alias(target = ['build-f1'], source = 'f1.out', action = foo) +f1 = env.Cat('f1.out', 'f1.in') +f2 = env.Cat('f2.out', 'f2.in') +f3 = env.Cat('f3.out', 'f3.in') +f4 = env.Cat('f4.out', 'f4.in') +f5 = env.Cat('f5.out', 'f5.in') +f6 = env.Cat('f6.out', 'f6.in') +env.Alias('build-all', [f1, f2, f3], foo) +env.Alias('build-add1', f3, foo) +env.Alias('build-add1', f2) +env.Alias('build-add2a', f4) +env.Alias('build-add2b', f5) +env.Alias(['build-add2a', 'build-add2b'], action=foo) +env.Alias('build-add3', f6) +env.Alias('build-add3', action=foo) +env.Alias('build-add3', action=bar) +""") + +test.write('f1.in', "f1.in 1\n") +test.write('f2.in', "f2.in 1\n") +test.write('f3.in', "f3.in 1\n") +test.write('f4.in', "f4.in 1\n") +test.write('f5.in', "f5.in 1\n") +test.write('f6.in', "f6.in 1\n") + +test.run(arguments = 'build-f1') + +test.must_match('f1.out', "f1.in 1\n") +test.must_match('foo', "foo(['build-f1'], ['f1.out'])\n") + +test.up_to_date(arguments = 'build-f1') + +test.write('f1.in', "f1.in 2\n") +test.unlink('foo') + +test.run(arguments = 'build-f1') + +test.must_match('f1.out', "f1.in 2\n") +test.must_match('foo', "foo(['build-f1'], ['f1.out'])\n") + +test.run(arguments = 'build-all') + +test.must_match('f1.out', "f1.in 2\n") +test.must_match('f2.out', "f2.in 1\n") +test.must_match('f3.out', "f3.in 1\n") +test.must_match('foo', "foo(['build-all'], ['f1.out', 'f2.out', 'f3.out'])\n") + +test.up_to_date(arguments = 'build-all') +test.up_to_date(arguments = 'build-add1') + +test.write('f1.in', "f1.in 3\n") +test.write('f3.in', "f3.in 2\n") +test.unlink('foo') + +test.run(arguments = 'build-add1') + +test.must_match('f1.out', "f1.in 2\n") +test.must_match('f2.out', "f2.in 1\n") +test.must_match('f3.out', "f3.in 2\n") +test.must_match('foo', "foo(['build-add1'], ['f3.out', 'f2.out'])\n") + +test.up_to_date(arguments = 'build-add1') + +test.run(arguments = 'build-add2a') + +test.must_match('f4.out', "f4.in 1\n") +test.must_not_exist('f5.out') +test.must_match('foo', "foo(['build-add2a'], ['f4.out'])\n") + +test.run(arguments = 'build-add2b') + +test.must_match('f5.out', "f5.in 1\n") +test.must_match('foo', "foo(['build-add2b'], ['f5.out'])\n") + +test.run(arguments = 'build-add3') + +test.must_match('f6.out', "f6.in 1\n") +test.must_match('foo', "foo(['build-add3'], ['f6.out'])\n") +test.must_match('bar', "bar(['build-add3'], ['f6.out'])\n") + +test.pass_test() diff --git a/test/Alias/errors.py b/test/Alias/errors.py new file mode 100644 index 0000000..f2ca950 --- /dev/null +++ b/test/Alias/errors.py @@ -0,0 +1,46 @@ +#!/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__" + +import os +import sys +import TestSCons +import TestCmd + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env=Environment() +TargetSignatures('content') +env.Alias('C', 'D') +env.Alias('B', 'C') +env.Alias('A', 'B') +""") + +test.run(arguments='A', + stderr="scons: *** Source `D' not found, needed by target `C'. Stop.\n", + status=2) + +test.pass_test() diff --git a/test/Alias/scanner.py b/test/Alias/scanner.py new file mode 100644 index 0000000..3ce14dd --- /dev/null +++ b/test/Alias/scanner.py @@ -0,0 +1,61 @@ +#!/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. +# + +""" +Test that an Alias of a node with a Scanner works. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import sys +import TestSCons +import TestCmd + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +def cat(env, source, target): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() + +XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') +env = Environment() +env.Append(BUILDERS = { 'XBuilder': XBuilder }) +f = env.XBuilder(source = ['file.x'], target = ['file.c']) +env.Alias(target = ['cfiles'], source = f) +Default(['cfiles']) +""") + +test.write('file.x', "file.x\n") + +test.run() + +test.fail_test(test.read('file.c') != "file.x\n") + +test.pass_test() -- cgit v0.12