From add807fa0aa088d65ef5e760b0f77643239df42d Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Wed, 17 Feb 2010 14:20:41 +0000 Subject: Generate an error message if a BUILDERS entry is set to something that isn't a Builder object, or known to generate a Builder object, or a callable. --- src/CHANGES.txt | 3 ++ src/engine/SCons/Builder.py | 10 ++++++ src/engine/SCons/Environment.py | 3 ++ src/engine/SCons/EnvironmentTests.py | 28 +++++++++------- src/engine/SCons/SConfTests.py | 3 +- test/Builder/not-a-Builder.py | 62 ++++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 test/Builder/not-a-Builder.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index e48c951..ec6ea95 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -18,6 +18,9 @@ RELEASE X.X.X - XXX - Make the messages for Configure checks of compilers consistent. + - Issue an error message if a BUILDERS entry is not a Builder + object or a callable wrapper. + From Rob Managan: - Update tex builder to handle the case where a \input{foo} diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 0f5bc76..6405da3 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -861,6 +861,16 @@ class CompositeBuilder(SCons.Util.Proxy): self.cmdgen.add_action(suffix, action) self.set_src_suffix(self.cmdgen.src_suffixes()) +def is_a_Builder(obj): + """"Returns True iff the specified obj is one of our Builder classes. + + The test is complicated a bit by the fact that CompositeBuilder + is a proxy, not a subclass of BuilderBase. + """ + return (isinstance(obj, BuilderBase) + or isinstance(obj, CompositeBuilder) + or callable(obj)) + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index f6ba969..f840dfc 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -152,6 +152,9 @@ def _set_BUILDERS(env, key, value): except KeyError: bd = BuilderDict(kwbd, env) env._dict[key] = bd + for k, v in value.items(): + if not SCons.Builder.is_a_Builder(v): + raise SCons.Errors.UserError('%s is not a Builder.' % repr(v)) bd.update(value) def _del_SCANNERS(env, key): diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 6d91620..aa80952 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -82,7 +82,7 @@ def diff_dict(d1, d2): called_it = {} built_it = {} -class Builder: +class Builder(SCons.Builder.BuilderBase): """A dummy Builder class for testing purposes. "Building" a target is simply setting a value in the dictionary. """ @@ -1628,9 +1628,11 @@ def exists(env): assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X'] assert env3['Y'] == {'y1': 10}, env3['Y'] - env4 = self.TestEnvironment(BUILDERS = {'z1' : 11}) - env4.Append(BUILDERS = {'z2' : 12}) - assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS'] + z1 = Builder() + z2 = Builder() + env4 = self.TestEnvironment(BUILDERS = {'z1' : z1}) + env4.Append(BUILDERS = {'z2' : z2}) + assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS'] assert hasattr(env4, 'z1') assert hasattr(env4, 'z2') @@ -1778,10 +1780,10 @@ def exists(env): assert not env1.Dictionary('ZZZ').has_key(5) # - env1 = self.TestEnvironment(BUILDERS = {'b1' : 1}) + env1 = self.TestEnvironment(BUILDERS = {'b1' : Builder()}) assert hasattr(env1, 'b1'), "env1.b1 was not set" assert env1.b1.object == env1, "b1.object doesn't point to env1" - env2 = env1.Clone(BUILDERS = {'b2' : 2}) + env2 = env1.Clone(BUILDERS = {'b2' : Builder()}) assert env2 is env2 assert env2 == env2 assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1" @@ -2281,9 +2283,11 @@ f5: \ assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X'] assert env3['Y'] == {'y1': 10}, env3['Y'] - env4 = self.TestEnvironment(BUILDERS = {'z1' : 11}) - env4.Prepend(BUILDERS = {'z2' : 12}) - assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS'] + z1 = Builder() + z2 = Builder() + env4 = self.TestEnvironment(BUILDERS = {'z1' : z1}) + env4.Prepend(BUILDERS = {'z2' : z2}) + assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS'] assert hasattr(env4, 'z1') assert hasattr(env4, 'z2') @@ -2399,9 +2403,11 @@ f5: \ env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc') assert env1 == env2, diff_env(env1, env2) - env3 = self.TestEnvironment(BUILDERS = {'b1' : 1}) + b1 = Builder() + b2 = Builder() + env3 = self.TestEnvironment(BUILDERS = {'b1' : b1}) assert hasattr(env3, 'b1'), "b1 was not set" - env3.Replace(BUILDERS = {'b2' : 2}) + env3.Replace(BUILDERS = {'b2' : b2}) assert not hasattr(env3, 'b1'), "b1 was not cleared" assert hasattr(env3, 'b2'), "b2 was not set" diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index 5315ea3..45b6eef 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -168,7 +168,8 @@ class SConfTestCase(unittest.TestCase): sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), log_file=self.test.workpath('config.log')) - class MyBuilder: + import SCons.Builder + class MyBuilder(SCons.Builder.BuilderBase): def __init__(self): self.prefix = '' self.suffix = '' diff --git a/test/Builder/not-a-Builder.py b/test/Builder/not-a-Builder.py new file mode 100644 index 0000000..355dc77 --- /dev/null +++ b/test/Builder/not-a-Builder.py @@ -0,0 +1,62 @@ +#!/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 the error when trying to configure a Builder with a non-Builder object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """\ +def mkdir(env, target, source): + return None +mkdir = 1 +env = Environment(BUILDERS={'mkdir': 1}) +env.mkdir(env.Dir('src'), None) +""") + +expect_stderr = """\ + +scons: \\*\\*\\* .* is not a Builder\\. +""" + test.python_file_line(SConstruct_path, 4) + +test.run(arguments='.', + stderr=expect_stderr, + status=2, + match=TestSCons.match_re) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12