diff options
Diffstat (limited to 'src')
25 files changed, 470 insertions, 116 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a47992a..9cf69e9 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,71 @@ +RELEASE 0.97.X - XXX + + From Mark Bertoglio: + + - Fix listing multiple projects in Visual Studio 7.[01] solution files, + including generating individual project GUIDs instead of re-using + the solution GUID. + + From Jean Brouwers: + + - Add /opt/SUNWspro/bin to the default execution PATH on Solaris. + + From Allan Erskine: + + - Only expect the Microsoft IDL compiler to emit *_p.c and *_data.c + files if the /proxy and /dlldata switches are used (respectively). + + From Steven Knight: + + - Have --debug=explain report if a target is being rebuilt because + AlwaysBuild() is specified (instead of "unknown reasons"). + + - Support {Get,Set}Option('help') to make it easier for SConscript + files to tell if a help option (-h, --help, etc.) has been specified. + + - Support {Get,Set}Option('random') so random-dependency interaction + with CacheDir() is controllable from SConscript files. + + - Push and retrieve built symlinks to/from a CacheDir() as actual + symlinks, not by copying the file contents. + + - Fix how the Action module handles stringifying the shared library + generator in the Tool/mingw.py module. + + - When generating a config.h file, print "#define HAVE_{FEATURE} 1" + instad of just "#define HAVE_{FEATURE}", for more compatibility + with Autoconf-style projects. + + - Fix expansion of $TARGET, $TARGETS, $SOURCE and $SOURCES keywords in + Visual C/C++ PDB file names. + + - Fix locating Visual C/C++ PDB files in build directories. + + - Support an env.AddMethod() method and an AddMethod() global function + for adding a new method, respectively, to a construction environment + or an arbitrary object (such as a class). + + From Leanid Nazdrynau: + + - When applying Tool modules after a construction environment has + already been created, don't overwrite existing $CFILESUFFIX and + $CXXFILESUFFIX value. + + From Jan Nijtmans: + + - Find Java anonymous classes when the next token after the name is + an open parenthesis. + + From Sohail Somani: + + - Add LaTeX scanner support for finding dependencies specified with + the \usepackage{} directive. + + + RELEASE 0.97 - Thu, 17 May 2007 08:59:41 -0500 From Steven Knight: diff --git a/src/RELEASE.txt b/src/RELEASE.txt index b19a39c..6dd66aa 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -25,6 +25,33 @@ RELEASE 0.97 - Thu, 12 Apr 2007 12:36:25 -0500 This is the eighth beta release of SCons. Please consult the CHANGES.txt file for a list of specific changes since last release. + Please note the following important changes since release 0.97: + + -- THE DEFAULT EXECUTION PATH FOR Solaris HAS CHANGED + + On Solaris systems, SCons now adds the "/opt/SUNWspro/bin" + directory to the default execution $PATH variable before the + "/usr/ccs/bin" directory. This was done to reflect the fact + that /opt/SUNWspro/ is the default for SUN tools, but it may + cause a different compiler to be used if you have compilers + installed in both directories. + + -- GENERATED config.h FILES NOW SAY "#define HAVE_{FEATURE} 1" + + When generating a "config.h" file, SCons now defines values that + record the existence of a feature with a "1" value: + + #define HAVE_FEATURE 1 + + Instead of printing the line without a "1", as it used to: + + #define HAVE_FEATURE + + This should not cause any problems in the normal use of "#ifdef + HAVE_{FEATURE}" statements interpreted by a C preprocessor, but + might cause a compatibility issue if a script or other utility + was looking for an exact match of the previous text. + Please note the following important changes since release 0.96.93: -- THE --debug=memoizer OPTION NOW REQUIRES PYTHON 2.2 OR LATER diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 438588b..a010c4a 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -515,9 +515,11 @@ class CommandGeneratorAction(ActionBase): def __str__(self): try: - env = self.presub_env or {} + env = self.presub_env except AttributeError: - env = {} + env = None + if env is None: + env = SCons.Defaults.DefaultEnvironment() act = self._generate([], [], env, 1) return str(act) diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 08373c3..87fb4b0 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1261,6 +1261,14 @@ class CommandGeneratorActionTestCase(unittest.TestCase): """Test the pre-substitution strings for command generator Actions """ def f(target, source, env, for_signature, self=self): + + # See if "env" is really a construction environment (or + # looks like one) by accessing the FindIxes attribute. + # (The Tool/mingw.py module has a generator that uses this, + # and the __str__() method used to cause problems by passing + # us a regular dictionary as a fallback.) + + env.FindIxes return "FOO" a = SCons.Action.CommandGeneratorAction(f) s = str(a) diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index ecc93c0..c4b1d1d 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -520,6 +520,9 @@ class BuilderBase: t.builder_set(self) new_targets.append(t) + orig_tlist = tlist[:] + orig_slist = slist[:] + target, source = self.emitter(target=tlist, source=slist, env=env) # Now delete the temporary builders that we attached to any @@ -533,8 +536,10 @@ class BuilderBase: # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. - tlist = env.arg2nodes(target, target_factory) - slist = env.arg2nodes(source, source_factory) + tlist = env.arg2nodes(target, target_factory, + target=orig_tlist, source=orig_slist) + slist = env.arg2nodes(source, source_factory, + target=orig_tlist, source=orig_slist) return tlist, slist diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index bb5a69a..3996d5c 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -33,6 +33,7 @@ def Func(): import os.path import sys import types +import StringIO import unittest import UserList @@ -43,6 +44,8 @@ import SCons.Builder import SCons.Environment import SCons.Errors +sys.stdout = StringIO.StringIO() + # Initial setup of the common environment for all tests, # a temporary working directory containing a # script for writing arguments to an output file. @@ -95,7 +98,7 @@ class Environment: source=None, dict=None, conv=None): return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv) - def arg2nodes(self, args, factory): + def arg2nodes(self, args, factory, **kw): global env_arg2nodes_called env_arg2nodes_called = 1 if not SCons.Util.is_List(args): @@ -1210,28 +1213,32 @@ class BuilderTestCase(unittest.TestCase): assert 'baz' in map(str, tgt.sources), map(str, tgt.sources) assert 'bar' in map(str, tgt.sources), map(str, tgt.sources) - # Test that, if an emitter sets a builder on the passed-in - # targets and passes back new targets, the new builder doesn't - # get overwritten. + def test_emitter_preserve_builder(self): + """Test an emitter not overwriting a newly-set builder""" + env = Environment() + new_builder = SCons.Builder.Builder(action='new') node = MyNode('foo8') new_node = MyNode('foo8.new') - def emit3(target, source, env, nb=new_builder, nn=new_node): + + def emit(target, source, env, nb=new_builder, nn=new_node): for t in target: t.builder = nb return [nn], source - builder3=SCons.Builder.Builder(action='foo', - emitter=emit3, - target_factory=MyNode, - source_factory=MyNode) - tgt = builder3(env, target=node, source='bar')[0] + builder=SCons.Builder.Builder(action='foo', + emitter=emit, + target_factory=MyNode, + source_factory=MyNode) + tgt = builder(env, target=node, source='bar')[0] assert tgt is new_node, tgt - assert tgt.builder is builder3, tgt.builder + assert tgt.builder is builder, tgt.builder assert node.builder is new_builder, node.builder - # Test use of a dictionary mapping file suffixes to - # emitter functions + def test_emitter_suffix_map(self): + """Test mapping file suffixes to emitter functions""" + env = Environment() + def emit4a(target, source, env): source = map(str, source) target = map(lambda x: 'emit4a-' + x[:-3], source) @@ -1240,61 +1247,86 @@ class BuilderTestCase(unittest.TestCase): source = map(str, source) target = map(lambda x: 'emit4b-' + x[:-3], source) return (target, source) - builder4 = SCons.Builder.Builder(action='foo', - emitter={'.4a':emit4a, - '.4b':emit4b}, - target_factory=MyNode, - source_factory=MyNode) - tgt = builder4(env, None, source='aaa.4a')[0] + + builder = SCons.Builder.Builder(action='foo', + emitter={'.4a':emit4a, + '.4b':emit4b}, + target_factory=MyNode, + source_factory=MyNode) + tgt = builder(env, None, source='aaa.4a')[0] assert str(tgt) == 'emit4a-aaa', str(tgt) - tgt = builder4(env, None, source='bbb.4b')[0] + tgt = builder(env, None, source='bbb.4b')[0] assert str(tgt) == 'emit4b-bbb', str(tgt) - tgt = builder4(env, None, source='ccc.4c')[0] + tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'ccc', str(tgt) def emit4c(target, source, env): source = map(str, source) target = map(lambda x: 'emit4c-' + x[:-3], source) return (target, source) - builder4.add_emitter('.4c', emit4c) - tgt = builder4(env, None, source='ccc.4c')[0] + + builder.add_emitter('.4c', emit4c) + tgt = builder(env, None, source='ccc.4c')[0] assert str(tgt) == 'emit4c-ccc', str(tgt) - # Test a list of emitter functions. - def emit5a(target, source, env): + def test_emitter_function_list(self): + """Test lists of emitter functions""" + env = Environment() + + def emit1a(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit5a-' + x[:-2], source) + target = target + map(lambda x: 'emit1a-' + x[:-2], source) return (target, source) - def emit5b(target, source, env): + def emit1b(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit5b-' + x[:-2], source) + target = target + map(lambda x: 'emit1b-' + x[:-2], source) return (target, source) - builder5 = SCons.Builder.Builder(action='foo', - emitter=[emit5a, emit5b], + builder1 = SCons.Builder.Builder(action='foo', + emitter=[emit1a, emit1b], node_factory=MyNode) - tgts = builder5(env, target='target-5', source='aaa.5') + tgts = builder1(env, target='target-1', source='aaa.1') tgts = map(str, tgts) - assert tgts == ['target-5', 'emit5a-aaa', 'emit5b-aaa'], tgts + assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts # Test a list of emitter functions through the environment. - def emit6a(target, source, env): + def emit2a(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit6a-' + x[:-2], source) + target = target + map(lambda x: 'emit2a-' + x[:-2], source) return (target, source) - def emit6b(target, source, env): + def emit2b(target, source, env): source = map(str, source) - target = target + map(lambda x: 'emit6b-' + x[:-2], source) + target = target + map(lambda x: 'emit2b-' + x[:-2], source) return (target, source) - builder6 = SCons.Builder.Builder(action='foo', + builder2 = SCons.Builder.Builder(action='foo', emitter='$EMITTERLIST', node_factory=MyNode) - env = Environment(EMITTERLIST = [emit6a, emit6b]) + env = Environment(EMITTERLIST = [emit2a, emit2b]) - tgts = builder6(env, target='target-6', source='aaa.6') + tgts = builder2(env, target='target-2', source='aaa.2') tgts = map(str, tgts) - assert tgts == ['target-6', 'emit6a-aaa', 'emit6b-aaa'], tgts + assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts + + def test_emitter_TARGET_SOURCE(self): + """Test use of $TARGET and $SOURCE in emitter results""" + + env = SCons.Environment.Environment() + + def emit(target, source, env): + return (target + ['${SOURCE}.s1', '${TARGET}.t1'], + source + ['${TARGET}.t2', '${SOURCE}.s2']) + + builder = SCons.Builder.Builder(action='foo', + emitter = emit, + node_factory = MyNode) + + targets = builder(env, target = 'TTT', source ='SSS') + sources = targets[0].sources + targets = map(str, targets) + sources = map(str, sources) + assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets + assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets def test_no_target(self): """Test deducing the target from the source.""" diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py index 81a8ee4..bb3be56 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -462,7 +462,7 @@ def _Have(context, key, have): key_up = re.sub('[^A-Z0-9_]', '_', key_up) context.havedict[key_up] = have if have == 1: - line = "#define %s\n" % key_up + line = "#define %s 1\n" % key_up elif have == 0: line = "/* #undef %s */\n" % key_up elif type(have) == IntType: diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index e5eb40c..94cdf74 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -327,7 +327,7 @@ class SubstitutionEnvironment: def items(self): return self._dict.items() - def arg2nodes(self, args, node_factory=_null, lookup_list=_null): + def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): if node_factory is _null: node_factory = self.fs.File if lookup_list is _null: @@ -351,7 +351,9 @@ class SubstitutionEnvironment: break if not n is None: if SCons.Util.is_String(n): - n = self.subst(n, raw=1) + # n = self.subst(n, raw=1, **kw) + kw['raw'] = 1 + n = apply(self.subst, (n,), kw) if node_factory: n = node_factory(n) if SCons.Util.is_List(n): @@ -359,7 +361,9 @@ class SubstitutionEnvironment: else: nodes.append(n) elif node_factory: - v = node_factory(self.subst(v, raw=1)) + # v = node_factory(self.subst(v, raw=1, **kw)) + kw['raw'] = 1 + v = node_factory(apply(self.subst, (v,), kw)) if SCons.Util.is_List(v): nodes.extend(v) else: @@ -473,6 +477,14 @@ class SubstitutionEnvironment: raise OSError("'%s' exited %d" % (command, status)) return out + def AddMethod(self, function, name=None): + """ + Adds the specified function as a method of this construction + environment with the specified name. If the name is omitted, + the default name is the name of the function itself. + """ + SCons.Util.AddMethod(self, function, name) + def Override(self, overrides): """ Produce a modified environment whose variables are overriden by @@ -1453,7 +1465,7 @@ class Base(SubstitutionEnvironment): def AlwaysBuild(self, *targets): tlist = [] for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.File)) + tlist.extend(self.arg2nodes(t, self.fs.Entry)) for t in tlist: t.set_always_build() return tlist diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 70f9026..a8b718d 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -357,6 +357,22 @@ class SubstitutionTestCase(unittest.TestCase): assert not hasattr(nodes[1], 'bbbb'), nodes[0] assert nodes[1].c == 1, nodes[1] + def test_arg2nodes_target_source(self): + """Test the arg2nodes method with target= and source= keywords + """ + targets = [DummyNode('t1'), DummyNode('t2')] + sources = [DummyNode('s1'), DummyNode('s2')] + env = SubstitutionEnvironment() + nodes = env.arg2nodes(['${TARGET}-a', + '${SOURCE}-b', + '${TARGETS[1]}-c', + '${SOURCES[1]}-d'], + DummyNode, + target=targets, + source=sources) + names = map(lambda n: n.name, nodes) + assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names + def test_gvars(self): """Test the base class gvars() method""" env = SubstitutionEnvironment() @@ -626,6 +642,38 @@ sys.exit(1) finally: sys.stderr = save_stderr + def test_AddMethod(self): + """Test the AddMethod() method""" + env = SubstitutionEnvironment(FOO = 'foo') + + def func(self): + return 'func-' + self['FOO'] + + assert not hasattr(env, 'func') + env.AddMethod(func) + r = env.func() + assert r == 'func-foo', r + + assert not hasattr(env, 'bar') + env.AddMethod(func, 'bar') + r = env.bar() + assert r == 'func-foo', r + + def func2(self, arg=''): + return 'func2-' + self['FOO'] + arg + + env.AddMethod(func2) + r = env.func2() + assert r == 'func2-foo', r + r = env.func2('-xxx') + assert r == 'func2-foo-xxx', r + + env.AddMethod(func2, 'func') + r = env.func() + assert r == 'func2-foo', r + r = env.func('-yyy') + assert r == 'func2-foo-yyy', r + def test_Override(self): "Test overriding construction variables" env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4) @@ -2390,22 +2438,29 @@ def generate(env): def test_AlwaysBuild(self): """Test the AlwaysBuild() method""" env = self.TestEnvironment(FOO='fff', BAR='bbb') - t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR') - assert t[0].__class__.__name__ == 'File' + t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR', + env.fs.Dir('dir'), env.fs.File('file')) + assert t[0].__class__.__name__ == 'Entry' assert t[0].path == 'a' assert t[0].always_build - assert t[1].__class__.__name__ == 'File' + assert t[1].__class__.__name__ == 'Entry' assert t[1].path == 'bfff' assert t[1].always_build - assert t[2].__class__.__name__ == 'File' + assert t[2].__class__.__name__ == 'Entry' assert t[2].path == 'c' assert t[2].always_build - assert t[3].__class__.__name__ == 'File' + assert t[3].__class__.__name__ == 'Entry' assert t[3].path == 'd' assert t[3].always_build - assert t[4].__class__.__name__ == 'File' + assert t[4].__class__.__name__ == 'Entry' assert t[4].path == 'bbb' assert t[4].always_build + assert t[5].__class__.__name__ == 'Dir' + assert t[5].path == 'dir' + assert t[5].always_build + assert t[6].__class__.__name__ == 'File' + assert t[6].path == 'file' + assert t[6].always_build def test_BuildDir(self): """Test the BuildDir() method""" diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index a16fee4..c9a1443 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -232,7 +232,10 @@ def CacheRetrieveFunc(target, source, env): return 1 fs.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) if SCons.Action.execute_actions: - fs.copy2(cachefile, t.path) + if fs.islink(cachefile): + fs.symlink(fs.readlink(cachefile), t.path) + else: + fs.copy2(cachefile, t.path) st = fs.stat(cachefile) fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 @@ -272,7 +275,10 @@ def CachePushFunc(target, source, env): tempfile = cachefile+'.tmp' try: - fs.copy2(t.path, tempfile) + if fs.islink(t.path): + fs.symlink(fs.readlink(t.path), tempfile) + else: + fs.copy2(t.path, tempfile) fs.rename(tempfile, cachefile) st = fs.stat(t.path) fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) @@ -2269,8 +2275,6 @@ class File(Base): self.binfo = self.gen_binfo(calc) return self._cur2() def _cur2(self): - if self.always_build: - return None if not self.exists(): # The file doesn't exist locally... r = self.rfile() diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 6644d7a..f550b5b 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -1046,32 +1046,27 @@ class Node: if not self.exists(): return "building `%s' because it doesn't exist\n" % self + if self.always_build: + return "rebuilding `%s' because AlwaysBuild() is specified\n" % self + old = self.get_stored_info() if old is None: return None old.prepare_dependencies() - def dictify(result, kids, sigs): - for k, s in zip(kids, sigs): - result[k] = s - try: - osig = {} - dictify(osig, old.bsources, old.bsourcesigs) - dictify(osig, old.bdepends, old.bdependsigs) - dictify(osig, old.bimplicit, old.bimplicitsigs) + old_bkids = old.bsources + old.bdepends + old.bimplicit + old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs except AttributeError: return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self new = self.get_binfo() - nsig = {} - dictify(nsig, new.bsources, new.bsourcesigs) - dictify(nsig, new.bdepends, new.bdependsigs) - dictify(nsig, new.bimplicit, new.bimplicitsigs) + new_bkids = new.bsources + new.bdepends + new.bimplicit + new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs - old_bkids = old.bsources + old.bdepends + old.bimplicit - new_bkids = new.bsources + new.bdepends + new.bimplicit + osig = dict(zip(old_bkids, old_bkidsigs)) + nsig = dict(zip(new_bkids, new_bkidsigs)) # The sources and dependencies we'll want to report are all stored # as relative paths to this target's directory, but we want to diff --git a/src/engine/SCons/Platform/sunos.py b/src/engine/SCons/Platform/sunos.py index ed9521e..5029860 100644 --- a/src/engine/SCons/Platform/sunos.py +++ b/src/engine/SCons/Platform/sunos.py @@ -41,4 +41,4 @@ def generate(env): env['MAXLINELENGTH'] = 1045320 env['PKGINFO'] = 'pkginfo' env['PKGCHK'] = '/usr/sbin/pkgchk' - env['ENV']['PATH'] = env['ENV']['PATH'] + ':/usr/ccs/bin' + env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index d875e6e..645a894 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -39,7 +39,7 @@ def LaTeXScanner(fs = SCons.Node.FS.default_fs): ds = LaTeX(name = "LaTeXScanner", suffixes = '$LATEXSUFFIXES', path_variable = 'TEXINPUTS', - regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography){([^}]*)}', + regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}', recursive = 0) return ds @@ -72,6 +72,10 @@ class LaTeX(SCons.Scanner.Classic): base, ext = os.path.splitext( filename ) if ext == "": filename = filename + '.bib' + if include[0] == 'usepackage': + base, ext = os.path.splitext( filename ) + if ext == "": + filename = filename + '.sty' return filename def sort_key(self, include): return SCons.Node.FS._my_normcase(self.latex_name(include)) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index ca7f68c..d80050d 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -40,7 +40,6 @@ import SCons.compat import os import os.path -import random import string import sys import time @@ -789,7 +788,7 @@ class OptParser(OptionParser): help="Read FILE as the top-level SConstruct file.") self.add_option('-h', '--help', action="store_true", default=0, - dest="help_msg", + dest="help", help="Print defined help message, or this one.") self.add_option("-H", "--help-options", @@ -988,13 +987,18 @@ class SConscriptSettableOptions: # This dictionary stores the defaults for all the SConscript # settable options, as well as indicating which options - # are SConscript settable. - self.settable = {'num_jobs':1, - 'max_drift':SCons.Node.FS.default_max_drift, - 'implicit_cache':0, - 'clean':0, - 'duplicate':'hard-soft-copy', - 'diskcheck':diskcheck_all} + # are SConscript settable (and gettable, which for options + # like 'help' is far more important than being settable). + self.settable = { + 'clean' : 0, + 'diskcheck' : diskcheck_all, + 'duplicate' : 'hard-soft-copy', + 'help' : 0, + 'implicit_cache' : 0, + 'max_drift' : SCons.Node.FS.default_max_drift, + 'num_jobs' : 1, + 'random' : 0, + } def get(self, name): if not self.settable.has_key(name): @@ -1118,7 +1122,7 @@ def _main(args, parser): scripts.append(sfile) if not scripts: - if options.help_msg: + if options.help: # There's no SConstruct, but they specified -h. # Give them the options usage now, before we fail # trying to read a non-existent SConstruct file. @@ -1225,7 +1229,7 @@ def _main(args, parser): fs.chdir(fs.Top) - if options.help_msg: + if ssoptions.get('help'): help_text = SCons.Script.help_text if help_text is None: # They specified -h, but there was no Help() inside the @@ -1341,6 +1345,7 @@ def _main(args, parser): if options.random: def order(dependencies): """Randomize the dependencies.""" + import random # This is cribbed from the implementation of # random.shuffle() in Python 2.X. d = dependencies diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index fde4997..e970989 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -131,6 +131,7 @@ call_stack = _SConscript.call_stack # Action = SCons.Action.Action +AddMethod = SCons.Util.AddMethod AllowSubstExceptions = SCons.Subst.SetAllowableExceptions BoolOption = SCons.Options.BoolOption Builder = SCons.Builder.Builder diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 9a5011b..acb8c9b 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -275,8 +275,9 @@ class Task: """ self.out_of_date = [] for t in self.targets: + t.disambiguate() try: - is_up_to_date = t.disambiguate().current() + is_up_to_date = not t.always_build and t.current() except EnvironmentError, e: raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) if is_up_to_date: diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 757306f..718851d 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -63,6 +63,7 @@ class Node: self.postprocessed = None self._bsig_val = None self._current_val = 0 + self.always_build = None def disambiguate(self): return self @@ -495,7 +496,10 @@ class TaskmasterTestCase(unittest.TestCase): n3 = Node("n3") c4 = Node("c4") c4._current_val = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], + a5 = Node("a5") + a5._current_val = 1 + a5.always_build = 1 + tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5], tasker = TaskGen) del ood[:] @@ -514,6 +518,9 @@ class TaskmasterTestCase(unittest.TestCase): t = tm.next_task() assert ood == [], ood + del ood[:] + t = tm.next_task() + assert ood == [a5], ood def test_make_ready_exception(self): """Test handling exceptions from Task.make_ready() diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 177d579..d340d5b 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -53,7 +53,7 @@ if java_parsing: # array declarations "[]"; # semi-colons; # periods. - _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.]|' + + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + r'[A-Za-z_][\w\.]*|/\*|\*/|\[\])') class OuterState: @@ -90,6 +90,7 @@ if java_parsing: try: return self.anonState except AttributeError: + self.outer_state = self ret = SkipState(1, AnonClassState(self)) self.anonState = ret return ret @@ -154,23 +155,36 @@ if java_parsing: class AnonClassState: """A state that looks for anonymous inner classes.""" - def __init__(self, outer_state): + def __init__(self, old_state): # outer_state is always an instance of OuterState - self.outer_state = outer_state - self.tokens_to_find = 2 + self.outer_state = old_state.outer_state + self.old_state = old_state + self.brace_level = 0 def parseToken(self, token): - # This is an anonymous class if and only if the next - # non-whitespace token is a bracket - if token == '\n': + # This is an anonymous class if and only if the next + # non-whitespace token is a bracket. Everything between + # braces should be parsed as normal java code. + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '\n': + return self + elif token == '(': + self.brace_level = self.brace_level + 1 + return self + if self.brace_level > 0: + if token == 'new': + # look further for anonymous inner class + return SkipState(1, AnonClassState(self)) + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == ')': + self.brace_level = self.brace_level - 1 return self if token == '{': - self.outer_state.openBracket() self.outer_state.addAnonClass() - elif token == '}': - self.outer_state.closeBracket() - elif token in ['"', "'"]: - return IgnoreState(token, self) - return self.outer_state + return self.old_state.parseToken(token) class SkipState: """A state that will skip a specified number of tokens before diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 853afd4..e848bf9 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -329,6 +329,30 @@ public class A { assert pkg_dir == None, pkg_dir assert classes == ['A$B', 'A'], classes + def test_anonymous_classes_with_parentheses(self): + """Test finding anonymous classes marked by parentheses""" + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +import java.io.File; + +public class Foo { + public static void main(String[] args) { + File f = new File( + new File("a") { + public String toString() { + return "b"; + } + } to String() + ) { + public String toString() { + return "c"; + } + }; + } +} +""") + assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes + + if __name__ == "__main__": diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index b2e2eff..dea77fd 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -316,7 +316,8 @@ def createCFileBuilders(env): emitter = {}, suffix = {None:'$CFILESUFFIX'}) env['BUILDERS']['CFile'] = c_file - env['CFILESUFFIX'] = '.c' + + env.SetDefault(CFILESUFFIX = '.c') try: cxx_file = env['BUILDERS']['CXXFile'] @@ -325,7 +326,7 @@ def createCFileBuilders(env): emitter = {}, suffix = {None:'$CXXFILESUFFIX'}) env['BUILDERS']['CXXFile'] = cxx_file - env['CXXFILESUFFIX'] = '.cc' + env.SetDefault(CXXFILESUFFIX = '.cc') return (c_file, cxx_file) diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py index 67579e9..811d573 100644 --- a/src/engine/SCons/Tool/midl.py +++ b/src/engine/SCons/Tool/midl.py @@ -33,6 +33,8 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import string + import SCons.Action import SCons.Builder import SCons.Defaults @@ -46,11 +48,17 @@ def midl_emitter(target, source, env): tlb = target[0] incl = base + '.h' interface = base + '_i.c' - proxy = base + '_p.c' - dlldata = base + '_data.c' + t = [tlb, incl, interface] - t = [tlb, incl, interface, proxy, dlldata] + midlcom = env['MIDLCOM'] + if string.find(midlcom, '/proxy') != -1: + proxy = base + '_p.c' + t.append(proxy) + if string.find(midlcom, '/dlldata') != -1: + dlldata = base + '_data.c' + t.append(dlldata) + return (t,source) idl_scanner = SCons.Scanner.IDL.IDLScan() diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 4b90e3d..c071aa7 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -45,10 +45,10 @@ import SCons.Tool.msvs import SCons.Util def pdbGenerator(env, target, source, for_signature): - if target and env.has_key('PDB') and env['PDB']: - return ['/PDB:%s'%target[0].File(env['PDB']).get_string(for_signature), - '/DEBUG'] - return None + try: + return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] + except (AttributeError, IndexError): + return None def windowsShlinkTargets(target, source, env, for_signature): listCmd = [] @@ -99,7 +99,9 @@ def windowsLibEmitter(target, source, env): "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) if env.has_key('PDB') and env['PDB']: - target.append(env['PDB']) + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + target.append(pdb) + target[0].attributes.pdb = pdb if not no_import_lib and \ not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): @@ -129,7 +131,9 @@ def prog_emitter(target, source, env): "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) if env.has_key('PDB') and env['PDB']: - target.append(env['PDB']) + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + target.append(pdb) + target[0].attributes.pdb = pdb return (target,source) diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index db8e8fd..138f920 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -1018,8 +1018,10 @@ class _GenerateV7DSW(_DSWGenerator): self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) else: - self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s.Build.0 = %s|%s\n' %(self.slnguid,variant,variant,platform,self.slnguid,variant,variant,platform)) + for p in self.dspfiles: + guid = _generateGUID(p, '') + self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) self.file.write('\tEndGlobalSection\n') diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index f1e856b..a5ea859 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -950,3 +950,79 @@ class Unbuffered: self.file.flush() def __getattr__(self, attr): return getattr(self.file, attr) + +# The original idea for AddMethod() and RenameFunction() come from the +# following post to the ActiveState Python Cookbook: +# +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# +# That code was a little fragile, though, so the following changes +# have been wrung on it: +# +# * Switched the installmethod() "object" and "function" arguments, +# so the order reflects that the left-hand side is the thing being +# "assigned to" and the right-hand side is the value being assigned. +# +# * Changed explicit type-checking to the "try: klass = object.__class__" +# block in installmethod() below so that it still works with the +# old-style classes that SCons uses. +# +# * Replaced the by-hand creation of methods and functions with use of +# the "new" module, as alluded to in Alex Martelli's response to the +# following Cookbook post: +# +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 + +def AddMethod(object, function, name = None): + """ + Adds either a bound method to an instance or an unbound method to + a class. If name is ommited the name of the specified function + is used by default. + Example: + a = A() + def f(self, x, y): + self.z = x + y + AddMethod(f, A, "add") + a.add(2, 4) + print a.z + AddMethod(lambda self, i: self.l[i], a, "listIndex") + print a.listIndex(5) + """ + import new + + if name is None: + name = function.func_name + else: + function = RenameFunction(function, name) + + try: + klass = object.__class__ + except AttributeError: + # "object" is really a class, so it gets an unbound method. + object.__dict__[name] = new.instancemethod(function, None, object) + else: + # "object" is really an instance, so it gets a bound method. + object.__dict__[name] = new.instancemethod(function, object, klass) + +def RenameFunction(function, name): + """ + Returns a function identical to the specified function, but with + the specified name. + """ + import new + + # Compatibility for Python 1.5 and 2.1. Can be removed in favor of + # passing function.func_defaults directly to new.function() once + # we base on Python 2.2 or later. + func_defaults = function.func_defaults + if func_defaults is None: + func_defaults = () + + return new.function(function.func_code, + function.func_globals, + name, + func_defaults) + +del __revision__ diff --git a/src/engine/SCons/compat/builtins.py b/src/engine/SCons/compat/builtins.py index a477a48..1124cf0 100644 --- a/src/engine/SCons/compat/builtins.py +++ b/src/engine/SCons/compat/builtins.py @@ -76,11 +76,13 @@ try: dict except NameError: # Pre-2.2 Python has no dict() keyword. - def dict(*arg, **kwargs): + def dict(seq=[], **kwargs): """ New dictionary initialization. """ - d = apply(types.DictType, arg) + d = {} + for k, v in seq: + d[k] = v d.update(kwargs) return d __builtin__.dict = dict |