summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CHANGES.txt62
-rw-r--r--src/engine/MANIFEST.in2
-rw-r--r--src/engine/SCons/Action.py16
-rw-r--r--src/engine/SCons/ActionTests.py15
-rw-r--r--src/engine/SCons/BuilderTests.py2
-rw-r--r--src/engine/SCons/Conftest.py1
-rw-r--r--src/engine/SCons/Defaults.py1
-rw-r--r--src/engine/SCons/Environment.py66
-rw-r--r--src/engine/SCons/EnvironmentTests.py9
-rw-r--r--src/engine/SCons/Executor.py4
-rw-r--r--src/engine/SCons/Job.py2
-rw-r--r--src/engine/SCons/Node/FS.py13
-rw-r--r--src/engine/SCons/Node/__init__.py10
-rw-r--r--src/engine/SCons/Options/BoolOption.py6
-rw-r--r--src/engine/SCons/Options/BoolOptionTests.py6
-rw-r--r--src/engine/SCons/Options/PackageOption.py14
-rw-r--r--src/engine/SCons/Options/PackageOptionTests.py5
-rw-r--r--src/engine/SCons/Options/__init__.py4
-rw-r--r--src/engine/SCons/PathList.py1
-rw-r--r--src/engine/SCons/Platform/darwin.py1
-rw-r--r--src/engine/SCons/Platform/win32.py1
-rw-r--r--src/engine/SCons/SConf.py11
-rw-r--r--src/engine/SCons/SConsign.py2
-rw-r--r--src/engine/SCons/Scanner/Dir.py2
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py64
-rw-r--r--src/engine/SCons/Scanner/__init__.py48
-rw-r--r--src/engine/SCons/Script/Main.py156
-rw-r--r--src/engine/SCons/Script/SConscript.py4
-rw-r--r--src/engine/SCons/Script/__init__.py10
-rw-r--r--src/engine/SCons/Subst.py9
-rw-r--r--src/engine/SCons/SubstTests.py29
-rw-r--r--src/engine/SCons/Taskmaster.py241
-rw-r--r--src/engine/SCons/TaskmasterTests.py24
-rw-r--r--src/engine/SCons/Tool/BitKeeper.py2
-rw-r--r--src/engine/SCons/Tool/JavaCommon.py6
-rw-r--r--src/engine/SCons/Tool/JavaCommonTests.py21
-rw-r--r--src/engine/SCons/Tool/ToolTests.py2
-rw-r--r--src/engine/SCons/Tool/__init__.py7
-rw-r--r--src/engine/SCons/Tool/c++.py4
-rw-r--r--src/engine/SCons/Tool/cc.py35
-rw-r--r--src/engine/SCons/Tool/cvf.py1
-rw-r--r--src/engine/SCons/Tool/dmd.py2
-rw-r--r--src/engine/SCons/Tool/dvi.py2
-rw-r--r--src/engine/SCons/Tool/f77.py1
-rw-r--r--src/engine/SCons/Tool/f90.py1
-rw-r--r--src/engine/SCons/Tool/f95.py1
-rw-r--r--src/engine/SCons/Tool/fortran.py1
-rw-r--r--src/engine/SCons/Tool/intelc.py2
-rw-r--r--src/engine/SCons/Tool/jar.py5
-rw-r--r--src/engine/SCons/Tool/mingw.py1
-rw-r--r--src/engine/SCons/Tool/msvs.py7
-rw-r--r--src/engine/SCons/Tool/mwcc.py4
-rw-r--r--src/engine/SCons/Tool/mwld.py4
-rw-r--r--src/engine/SCons/Tool/pdf.py2
-rw-r--r--src/engine/SCons/Tool/qt.py13
-rw-r--r--src/engine/SCons/Tool/swig.py4
-rw-r--r--src/engine/SCons/Tool/tex.py2
-rw-r--r--src/engine/SCons/Tool/zip.py1
-rw-r--r--src/engine/SCons/Util.py54
-rw-r--r--src/engine/SCons/UtilTests.py6
-rw-r--r--src/engine/SCons/Warnings.py7
-rw-r--r--src/engine/SCons/__init__.py2
-rw-r--r--src/engine/SCons/compat/__init__.py19
-rw-r--r--src/engine/SCons/compat/_sets.py577
-rw-r--r--src/engine/SCons/compat/_sets15.py159
-rw-r--r--src/engine/SCons/compat/_subprocess.py24
-rw-r--r--src/engine/SCons/cpp.py1
-rw-r--r--src/script/scons-post-install.py76
-rw-r--r--src/script/scons.bat1
-rw-r--r--src/setup.cfg1
-rw-r--r--src/setup.py31
-rw-r--r--src/test_pychecker.py145
-rw-r--r--src/test_strings.py11
73 files changed, 1774 insertions, 312 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index c4671e5..b17ba4e 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -10,29 +10,91 @@
RELEASE 0.97 - XXX
+ From Anatoly:
+
+ - Add the scons.org URL and a package description to the setup.py
+ arguments.
+
+ - Have the Windows installer add a registry entry for scons.bat in the
+ "App Paths" key, so scons.bat can be executed without adding the
+ directory to the %PATH%. (Python itself works this way.)
+
From Anonymous:
- Fix looking for default paths in Visual Studio 8.0 (and later).
+ - Add -lm to the list of default D libraries for linking.
+
From Matt Doar:
- Provide a more complete write-your-own-Scanner example in the man page.
+ From Ralf W. Grosse-Kunstleve:
+
+ - Contributed upstream Python change to our copied subprocess.py module
+ for more efficient standard input processing.
+
From Steven Knight:
- Fix the Node.FS.Base.rel_path() method when the two nodes are on
different drive letters. (This caused an infinite loop when
trying to write .sconsign files.)
+ - Fully support Scanners that use a dictionary to map file suffixes
+ to other scanners.
+
+ - Support delayed evaluation of the $SPAWN variable to allow selection
+ of a function via ${} string expansions.
+
+ - Add --srcdir as a synonym for -Y/--repository.
+
+ - Document limitations of #include "file.h" with Repository().
+
+ - Fix use of a toolpath under the source directory of a BuildDir().
+
+ - Fix env.Install() with a file name portion that begins with '#'.
+
+ - Fix ParseConfig()'s handling of multiple options in a string that's
+ replaced a *FLAGS construction variable.
+
+ - Have the C++ tools initialize common C compilation variables ($CCFLAGS,
+ $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded.
+
+ From Leanid Nazdrynau:
+
+ - Fix detection of Java anonymous classes if a newline precedes the
+ opening brace.
+
From Gary Oberbrunner:
- Document use of ${} to execute arbitrary Python code.
+ - Add support for:
+ 1) automatically adding a site_scons subdirectory (in the top-level
+ SConstruct directory) to sys.path (PYTHONPATH);
+ 2) automatically importing site_scons/site_init.py;
+ 3) automatically adding site_scons/site_tools to the toolpath.
+
From John Pye:
- Change ParseConfig() to preserve white space in arguments passed in
as a list.
+ From a smith:
+
+ - Fix adding explicitly-named Java inner class files (and any
+ other file names that may contain a '$') to Jar files.
+
+ From David Vitek:
+
+ - Add a NoCache() function to mark targets as unsuitable for propagating
+ to (or retrieving from) a CacheDir().
+
+ From Ben Webb:
+
+ - If the swig -noproxy option is used, it won't generate a .py file,
+ so don't emit it as a target that we expect to be built.
+
RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 029217e..2484877 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -2,6 +2,8 @@ SCons/__init__.py
SCons/Action.py
SCons/Builder.py
SCons/compat/__init__.py
+SCons/compat/_sets.py
+SCons/compat/_sets15.py
SCons/compat/_subprocess.py
SCons/compat/_UserString.py
SCons/compat/builtins.py
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 503dc9f..dd7009c 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -100,12 +100,12 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import dis
import os
import os.path
-import re
import string
import sys
from SCons.Debug import logInstanceCreation
import SCons.Errors
+import SCons.Executor
import SCons.Util
class _Null:
@@ -403,7 +403,8 @@ class CommandAction(_ActionAction):
def strfunction(self, target, source, env):
if not self.cmdstr is None:
- c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c:
return c
cmd_list, ignore, silent = self.process(target, source, env)
@@ -421,7 +422,10 @@ class CommandAction(_ActionAction):
externally.
"""
from SCons.Subst import escape_list
- from SCons.Util import is_String, is_List, flatten
+ import SCons.Util
+ flatten = SCons.Util.flatten
+ is_String = SCons.Util.is_String
+ is_List = SCons.Util.is_List
try:
shell = env['SHELL']
@@ -432,6 +436,9 @@ class CommandAction(_ActionAction):
spawn = env['SPAWN']
except KeyError:
raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+ else:
+ if is_String(spawn):
+ spawn = env.subst(spawn, raw=1, conv=lambda x: x)
escape = env.get('ESCAPE', lambda x: x)
@@ -618,7 +625,8 @@ class FunctionAction(_ActionAction):
if self.cmdstr is None:
return None
if not self.cmdstr is _null:
- c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c:
return c
def array(a):
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 2c6d915..8339610 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -135,11 +135,13 @@ class Environment:
for k, v in kw.items():
self.d[k] = v
# Just use the underlying scons_subst*() utility methods.
- def subst(self, strSubst, raw=0, target=[], source=[]):
- return SCons.Subst.scons_subst(strSubst, self, raw, target, source, self.d)
+ def subst(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst(strSubst, self, raw,
+ target, source, self.d, conv=conv)
subst_target_source = subst
- def subst_list(self, strSubst, raw=0, target=[], source=[]):
- return SCons.Subst.scons_subst_list(strSubst, self, raw, target, source, self.d)
+ def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst_list(strSubst, self, raw,
+ target, source, self.d, conv=conv)
def __getitem__(self, item):
return self.d[item]
def __setitem__(self, item, value):
@@ -1151,6 +1153,11 @@ class CommandActionTestCase(unittest.TestCase):
assert t.executed == [ 'xyzzy' ], t.executed
a = SCons.Action.CommandAction(["xyzzy"])
+ e = Environment(SPAWN = '$FUNC', FUNC = func)
+ a([], [], e)
+ assert t.executed == [ 'xyzzy' ], t.executed
+
+ a = SCons.Action.CommandAction(["xyzzy"])
e = Environment(SPAWN = func, SHELL = 'fake shell')
a([], [], e)
assert t.executed == [ 'xyzzy' ], t.executed
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index acf0722..77ac9f4 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -864,7 +864,7 @@ class BuilderTestCase(unittest.TestCase):
def func(self):
pass
- scanner = SCons.Scanner.Scanner(func, name='fooscan')
+ scanner = SCons.Scanner.Base(func, name='fooscan')
b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py
index ddb1a99..81a8ee4 100644
--- a/src/engine/SCons/Conftest.py
+++ b/src/engine/SCons/Conftest.py
@@ -341,7 +341,6 @@ def CheckLib(context, libs, func_name = None, header = None,
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
Returns an empty string for success, an error message for failure.
"""
- from SCons.Debug import Trace
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
if context.headerfilename:
includetext = '#include "%s"' % context.headerfilename
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 96c3cf8..11bace3 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -40,7 +40,6 @@ import os
import os.path
import shutil
import stat
-import string
import time
import types
import sys
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 6c392a5..e5eb40c 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -38,7 +38,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import copy
import os
import os.path
-import popen2
import string
from UserDict import UserDict
@@ -47,6 +46,7 @@ import SCons.Builder
from SCons.Debug import logInstanceCreation
import SCons.Defaults
import SCons.Errors
+import SCons.Memoize
import SCons.Node
import SCons.Node.Alias
import SCons.Node.FS
@@ -54,7 +54,6 @@ import SCons.Node.Python
import SCons.Platform
import SCons.SConsign
import SCons.Sig
-import SCons.Sig.TimeStamp
import SCons.Subst
import SCons.Tool
import SCons.Util
@@ -508,17 +507,17 @@ class SubstitutionEnvironment:
the result of that evaluation is then added to the dict.
"""
dict = {
- 'ASFLAGS' : [],
- 'CFLAGS' : [],
- 'CCFLAGS' : [],
+ 'ASFLAGS' : SCons.Util.CLVar(''),
+ 'CFLAGS' : SCons.Util.CLVar(''),
+ 'CCFLAGS' : SCons.Util.CLVar(''),
'CPPDEFINES' : [],
- 'CPPFLAGS' : [],
+ 'CPPFLAGS' : SCons.Util.CLVar(''),
'CPPPATH' : [],
- 'FRAMEWORKPATH' : [],
- 'FRAMEWORKS' : [],
+ 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
+ 'FRAMEWORKS' : SCons.Util.CLVar(''),
'LIBPATH' : [],
'LIBS' : [],
- 'LINKFLAGS' : [],
+ 'LINKFLAGS' : SCons.Util.CLVar(''),
'RPATH' : [],
}
@@ -620,7 +619,7 @@ class SubstitutionEnvironment:
if arg[2:]:
append_define(arg[2:])
else:
- appencd_next_arg_to = 'CPPDEFINES'
+ append_next_arg_to = 'CPPDEFINES'
elif arg == '-framework':
append_next_arg_to = 'FRAMEWORKS'
elif arg[:14] == '-frameworkdir=':
@@ -665,7 +664,7 @@ class SubstitutionEnvironment:
apply(self.Append, (), args)
return self
for key, value in args.items():
- if value == '':
+ if not value:
continue
try:
orig = self[key]
@@ -673,10 +672,24 @@ class SubstitutionEnvironment:
orig = value
else:
if not orig:
- orig = []
- elif not SCons.Util.is_List(orig):
- orig = [orig]
- orig = orig + value
+ orig = value
+ elif value:
+ # Add orig and value. The logic here was lifted from
+ # part of env.Append() (see there for a lot of comments
+ # about the order in which things are tried) and is
+ # used mainly to handle coercion of strings to CLVar to
+ # "do the right thing" given (e.g.) an original CCFLAGS
+ # string variable like '-pipe -Wall'.
+ try:
+ orig = orig + value
+ except (KeyError, TypeError):
+ try:
+ add_to_orig = orig.append
+ except AttributeError:
+ value.insert(0, orig)
+ orig = value
+ else:
+ add_to_orig(value)
t = []
if key[-4:] == 'PATH':
### keep left-most occurence
@@ -1314,12 +1327,15 @@ class Base(SubstitutionEnvironment):
del kw[k]
apply(self.Replace, (), kw)
+ def _find_toolpath_dir(self, tp):
+ return self.fs.Dir(self.subst(tp)).srcnode().abspath
+
def Tool(self, tool, toolpath=None, **kw):
if SCons.Util.is_String(tool):
tool = self.subst(tool)
if toolpath is None:
toolpath = self.get('toolpath', [])
- toolpath = map(self.subst, toolpath)
+ toolpath = map(self._find_toolpath_dir, toolpath)
tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
tool(self)
@@ -1514,6 +1530,15 @@ class Base(SubstitutionEnvironment):
t.set_noclean()
return tlist
+ def NoCache(self, *targets):
+ """Tags a target so that it will not be cached"""
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_nocache()
+ return tlist
+
def Entry(self, name, *args, **kw):
"""
"""
@@ -1575,7 +1600,10 @@ class Base(SubstitutionEnvironment):
tgt = []
for dnode in dnodes:
for src in sources:
- target = self.fs.Entry(src.name, dnode)
+ # Prepend './' so the lookup doesn't interpret an initial
+ # '#' on the file name portion as meaning the Node should
+ # be relative to the top-level SConstruct directory.
+ target = self.fs.Entry('.'+os.sep+src.name, dnode)
tgt.extend(InstallBuilder(self, target, src))
return tgt
@@ -1631,7 +1659,7 @@ class Base(SubstitutionEnvironment):
arg = self.subst(arg)
nargs.append(arg)
nkw = self.subst_kw(kw)
- return apply(SCons.Scanner.Scanner, nargs, nkw)
+ return apply(SCons.Scanner.Base, nargs, nkw)
def SConsignFile(self, name=".sconsign", dbm_module=None):
if not name is None:
@@ -1746,7 +1774,7 @@ class OverrideEnvironment(Base):
def __getattr__(self, name):
return getattr(self.__dict__['__subject'], name)
def __setattr__(self, name, value):
- return setattr(self.__dict__['__subject'], name, value)
+ setattr(self.__dict__['__subject'], name, value)
# Methods that make this class act like a dictionary.
def __getitem__(self, key):
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index c015bc1..70f9026 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -695,7 +695,7 @@ sys.exit(1)
"-pthread " + \
"-mno-cygwin -mwindows " + \
"-arch i386 -isysroot /tmp +DD64 " + \
- "-DFOO -DBAR=value"
+ "-DFOO -DBAR=value -D BAZ"
d = env.ParseFlags(s)
@@ -705,7 +705,7 @@ sys.exit(1)
'-pthread', '-mno-cygwin',
('-arch', 'i386'), ('-isysroot', '/tmp'),
'+DD64'], d['CCFLAGS']
- assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value']], d['CPPDEFINES']
+ assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
assert d['CPPPATH'] == ['/usr/include/fum', 'bar'], d['CPPPATH']
assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
@@ -725,7 +725,7 @@ sys.exit(1)
"""
env = SubstitutionEnvironment()
env.MergeFlags('')
- assert env['CCFLAGS'] == [], env['CCFLAGS']
+ assert not env.has_key('CCFLAGS'), env['CCFLAGS']
env.MergeFlags('-X')
assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
env.MergeFlags('-X')
@@ -2805,6 +2805,9 @@ def generate(env):
for tnode in tgt:
assert tnode.builder == InstallBuilder
+ tgt = env.Install('export', 'subdir/#file')
+ assert str(tgt[0]) == os.path.normpath('export/#file'), str(tgt[0])
+
env.File('export/foo1')
exc_caught = None
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 4b15010..12114bc 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -33,7 +33,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import string
from SCons.Debug import logInstanceCreation
-import SCons.Util
+import SCons.Memoize
class Executor:
@@ -63,8 +63,10 @@ class Executor:
self._memo = {}
def set_action_list(self, action):
+ import SCons.Util
if not SCons.Util.is_List(action):
if not action:
+ import SCons.Errors
raise SCons.Errors.UserError, "Executor must have an action."
action = [action]
self.action_list = action
diff --git a/src/engine/SCons/Job.py b/src/engine/SCons/Job.py
index 8dde905..9832f14 100644
--- a/src/engine/SCons/Job.py
+++ b/src/engine/SCons/Job.py
@@ -170,7 +170,7 @@ else:
self.resultsQueue = Queue.Queue(0)
# Create worker threads
- for i in range(num):
+ for _ in range(num):
Worker(self.requestQueue, self.resultsQueue)
def put(self, obj):
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 9e0e6f6..4d269d2 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -47,6 +47,7 @@ import cStringIO
import SCons.Action
from SCons.Debug import logInstanceCreation
import SCons.Errors
+import SCons.Memoize
import SCons.Node
import SCons.Subst
import SCons.Util
@@ -249,6 +250,8 @@ CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None)
def CachePushFunc(target, source, env):
t = target[0]
+ if t.nocache:
+ return
fs = t.fs
cachedir, cachefile = t.cachepath()
if fs.exists(cachefile):
@@ -829,7 +832,7 @@ class Entry(Base):
morph this Entry."""
try:
self = self.disambiguate(must_exist=1)
- except SCons.Errors.UserError, e:
+ except SCons.Errors.UserError:
# There was nothing on disk with which to disambiguate
# this entry. Leave it as an Entry, but return a null
# string so calls to get_contents() in emitters and the
@@ -1054,7 +1057,7 @@ class FS(LocalFS):
path_norm = string.split(_my_normcase(name), os.sep)
first_orig = path_orig.pop(0) # strip first element
- first_norm = path_norm.pop(0) # strip first element
+ unused = path_norm.pop(0) # strip first element
drive, path_first = os.path.splitdrive(first_orig)
if path_first:
@@ -1477,9 +1480,11 @@ class Dir(Base):
return result
def get_env_scanner(self, env, kw={}):
+ import SCons.Defaults
return SCons.Defaults.DirEntryScanner
def get_target_scanner(self):
+ import SCons.Defaults
return SCons.Defaults.DirEntryScanner
def get_found_includes(self, env, scanner, path):
@@ -2033,6 +2038,8 @@ class File(Base):
Returns true iff the node was successfully retrieved.
"""
+ if self.nocache:
+ return None
b = self.is_derived()
if not b and not self.has_src_builder():
return None
@@ -2298,7 +2305,7 @@ class File(Base):
return str(self.rfile())
def cachepath(self):
- if not self.fs.CachePath:
+ if self.nocache or not self.fs.CachePath:
return None, None
ninfo = self.get_binfo().ninfo
if not hasattr(ninfo, 'bsig'):
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 405010c..fa682a2 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -52,6 +52,7 @@ import UserList
from SCons.Debug import logInstanceCreation
import SCons.Executor
+import SCons.Memoize
import SCons.SConsign
import SCons.Util
@@ -202,14 +203,13 @@ class Node:
self.state = no_state
self.precious = None
self.noclean = 0
+ self.nocache = 0
self.always_build = None
self.found_includes = {}
self.includes = None
self.attributes = self.Attrs() # Generic place to stick information about the Node.
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
- self.pre_actions = []
- self.post_actions = []
self.linked = 0 # is this node linked to the build directory?
self.clear_memoized_values()
@@ -765,6 +765,12 @@ class Node:
# output in Util.py can use it as an index.
self.noclean = noclean and 1 or 0
+ def set_nocache(self, nocache = 1):
+ """Set the Node's nocache value."""
+ # Make sure nocache is an integer so the --debug=stree
+ # output in Util.py can use it as an index.
+ self.nocache = nocache and 1 or 0
+
def set_always_build(self, always_build = 1):
"""Set the Node's always_build value."""
self.always_build = always_build
diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py
index f38bf02..80b607d 100644
--- a/src/engine/SCons/Options/BoolOption.py
+++ b/src/engine/SCons/Options/BoolOption.py
@@ -36,18 +36,16 @@ Usage example:
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-__all__ = ('BoolOption', 'True', 'False')
+__all__ = ('BoolOption')
import string
+import SCons.compat
import SCons.Errors
__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' )
__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none')
-# we need this since SCons should work version indepentant
-True, False = 1, 0
-
def _text2bool(val):
"""
diff --git a/src/engine/SCons/Options/BoolOptionTests.py b/src/engine/SCons/Options/BoolOptionTests.py
index 845f251..07b5b79 100644
--- a/src/engine/SCons/Options/BoolOptionTests.py
+++ b/src/engine/SCons/Options/BoolOptionTests.py
@@ -23,6 +23,8 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import sys
import unittest
@@ -91,8 +93,8 @@ class BoolOptionTestCase(unittest.TestCase):
o = opts.options[0]
env = {
- 'T' : SCons.Options.True,
- 'F' : SCons.Options.False,
+ 'T' : True,
+ 'F' : False,
'N' : 'xyzzy',
}
diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py
index 9ecb42a..3b4f0ce 100644
--- a/src/engine/SCons/Options/PackageOption.py
+++ b/src/engine/SCons/Options/PackageOption.py
@@ -52,15 +52,15 @@ Usage example:
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-__all__ = ('PackageOption', 'True', 'False')
+__all__ = ('PackageOption')
import string
-from BoolOption import True, False
+import SCons.compat
import SCons.Errors
-__enable_strings = (str(True), 'yes', 'true', 'on', 'enable', 'search')
-__disable_strings = (str(False), 'no', 'false', 'off', 'disable')
+__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search')
+__disable_strings = ('0', 'no', 'false', 'off', 'disable')
def _converter(val):
"""
@@ -78,12 +78,10 @@ def _validator(key, val, env, searchfunc):
"""
# todo: write validator, check for path
import os
- if env[key] == False:
- pass
- elif env[key] == True:
+ if env[key] is True:
if searchfunc:
env[key] = searchfunc(key, val)
- elif not os.path.exists(val):
+ elif env[key] and not os.path.exists(val):
raise SCons.Errors.UserError(
'Path does not exist for option %s: %s' % (key, val))
diff --git a/src/engine/SCons/Options/PackageOptionTests.py b/src/engine/SCons/Options/PackageOptionTests.py
index 7c868e5..68f14e5 100644
--- a/src/engine/SCons/Options/PackageOptionTests.py
+++ b/src/engine/SCons/Options/PackageOptionTests.py
@@ -23,12 +23,13 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import sys
import unittest
import SCons.Errors
import SCons.Options
-from SCons.Options.BoolOption import True, False
import TestCmd
@@ -96,7 +97,7 @@ class PackageOptionTestCase(unittest.TestCase):
o = opts.options[0]
- env = {'F':0, 'T':1, 'X':'x'}
+ env = {'F':False, 'T':True, 'X':'x'}
exists = test.workpath('exists')
does_not_exist = test.workpath('does_not_exist')
diff --git a/src/engine/SCons/Options/__init__.py b/src/engine/SCons/Options/__init__.py
index 5c30be6..66c2143 100644
--- a/src/engine/SCons/Options/__init__.py
+++ b/src/engine/SCons/Options/__init__.py
@@ -36,7 +36,7 @@ import SCons.Errors
import SCons.Util
import SCons.Warnings
-from BoolOption import BoolOption, True, False # okay
+from BoolOption import BoolOption # okay
from EnumOption import EnumOption # okay
from ListOption import ListOption # naja
from PackageOption import PackageOption # naja
@@ -227,8 +227,6 @@ class Options:
of the options.
"""
- help_text = ""
-
if sort:
options = self.options[:]
options.sort(lambda x,y,func=sort: func(x.key,y.key))
diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py
index b757bd3..81b8135 100644
--- a/src/engine/SCons/PathList.py
+++ b/src/engine/SCons/PathList.py
@@ -35,6 +35,7 @@ Do the Right Thing (almost) regardless of how the variable is specified.
import os
import string
+import SCons.Memoize
import SCons.Util
#
diff --git a/src/engine/SCons/Platform/darwin.py b/src/engine/SCons/Platform/darwin.py
index 7883795..fc4c773 100644
--- a/src/engine/SCons/Platform/darwin.py
+++ b/src/engine/SCons/Platform/darwin.py
@@ -33,7 +33,6 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import posix
-import os
def generate(env):
posix.generate(env)
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
index 148d9df..8d35a8d 100644
--- a/src/engine/SCons/Platform/win32.py
+++ b/src/engine/SCons/Platform/win32.py
@@ -93,7 +93,6 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
try:
ret = exitvalmap[e[0]]
except KeyError:
- result = 127
sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1]))
if stderr != None:
stderr.write("scons: %s: %s\n" % (cmd, e[1]))
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
index 5c20f26..21305b9 100644
--- a/src/engine/SCons/SConf.py
+++ b/src/engine/SCons/SConf.py
@@ -39,6 +39,7 @@ import types
import SCons.Action
import SCons.Builder
import SCons.Errors
+import SCons.Job
import SCons.Node.FS
import SCons.Taskmaster
import SCons.Util
@@ -141,7 +142,6 @@ def _createSource( target, source, env ):
fd.write(source[0].get_contents())
fd.close()
def _stringSource( target, source, env ):
- import string
return (str(target[0]) + ' <-\n |' +
string.replace( source[0].get_contents(),
'\n', "\n |" ) )
@@ -188,6 +188,11 @@ class Streamer:
Return everything written to orig since the Streamer was created.
"""
return self.s.getvalue()
+
+ def flush(self):
+ if self.orig:
+ self.orig.flush()
+ self.s.flush()
class SConfBuildTask(SCons.Taskmaster.Task):
@@ -229,7 +234,6 @@ class SConfBuildTask(SCons.Taskmaster.Task):
except AttributeError:
# Earlier versions of Python don't have sys.excepthook...
def excepthook(type, value, tb):
- import traceback
traceback.print_tb(tb)
print type, value
apply(excepthook, self.exc_info())
@@ -597,7 +601,8 @@ class SConf:
else:
_ac_config_logs[self.logfile] = None
log_mode = "w"
- self.logstream = open(str(self.logfile), log_mode)
+ fp = open(str(self.logfile), log_mode)
+ self.logstream = SCons.Util.Unbuffered(fp)
# logfile may stay in a build directory, so we tell
# the build system not to override it with a eventually
# existing file with the same name in the source directory
diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py
index 67b2aff..dcd6979 100644
--- a/src/engine/SCons/SConsign.py
+++ b/src/engine/SCons/SConsign.py
@@ -32,8 +32,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import cPickle
import os
import os.path
-import string
-import time
import SCons.dblite
import SCons.Sig
diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py
index fb23d1b..535150a 100644
--- a/src/engine/SCons/Scanner/Dir.py
+++ b/src/engine/SCons/Scanner/Dir.py
@@ -23,8 +23,6 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import string
-
import SCons.Node.FS
import SCons.Scanner
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index bd8546f..30dc1df 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -82,29 +82,37 @@ class FindPathDirsTestCase(unittest.TestCase):
class ScannerTestCase(unittest.TestCase):
def test_creation(self):
- """Test creation of Scanner objects through the Scanner() function"""
+ """Test creation of Scanner objects"""
def func(self):
pass
- s = SCons.Scanner.Scanner(func)
+ s = SCons.Scanner.Base(func)
+ assert isinstance(s, SCons.Scanner.Base), s
+ s = SCons.Scanner.Base({})
assert isinstance(s, SCons.Scanner.Base), s
- s = SCons.Scanner.Scanner({})
- assert isinstance(s, SCons.Scanner.Selector), s
- s = SCons.Scanner.Scanner(func, name='fooscan')
+ s = SCons.Scanner.Base(func, name='fooscan')
assert str(s) == 'fooscan', str(s)
- s = SCons.Scanner.Scanner({}, name='barscan')
+ s = SCons.Scanner.Base({}, name='barscan')
assert str(s) == 'barscan', str(s)
- s = SCons.Scanner.Scanner(func, name='fooscan', argument=9)
+ s = SCons.Scanner.Base(func, name='fooscan', argument=9)
assert str(s) == 'fooscan', str(s)
assert s.argument == 9, s.argument
- s = SCons.Scanner.Scanner({}, name='fooscan', argument=888)
+ s = SCons.Scanner.Base({}, name='fooscan', argument=888)
assert str(s) == 'fooscan', str(s)
assert s.argument == 888, s.argument
class BaseTestCase(unittest.TestCase):
+ class skey_node:
+ def __init__(self, key):
+ self.key = key
+ def scanner_key(self):
+ return self.key
+ def rexists(self):
+ return 1
+
def func(self, filename, env, target, *args):
self.filename = filename
self.env = env
@@ -132,6 +140,29 @@ class BaseTestCase(unittest.TestCase):
else:
self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
+ def test___call__dict(self):
+ """Test calling Scanner.Base objects with a dictionary"""
+ called = []
+ def s1func(node, env, path, called=called):
+ called.append('s1func')
+ called.append(node)
+ return []
+ def s2func(node, env, path, called=called):
+ called.append('s2func')
+ called.append(node)
+ return []
+ s1 = SCons.Scanner.Base(s1func)
+ s2 = SCons.Scanner.Base(s2func)
+ selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2})
+ nx = self.skey_node('.x')
+ env = DummyEnvironment()
+ selector(nx, env, [])
+ assert called == ['s1func', nx], called
+ del called[:]
+ ny = self.skey_node('.y')
+ selector(ny, env, [])
+ assert called == ['s2func', ny], called
+
def test_path(self):
"""Test the Scanner.Base path() method"""
def pf(env, cwd, target, source, argument=None):
@@ -277,6 +308,23 @@ class BaseTestCase(unittest.TestCase):
s = scanner.select('.x')
assert s is scanner, s
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.x'))
+ assert s == 1, s
+ s = selector.select(self.skey_node('.y'))
+ assert s == 2, s
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+
+ def test_add_scanner(self):
+ """Test the Scanner.Base add_scanner() method"""
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+ selector.add_scanner('.z', 3)
+ s = selector.select(self.skey_node('.z'))
+ assert s == 3, s
+
def test___str__(self):
"""Test the Scanner.Base __str__() method"""
scanner = SCons.Scanner.Base(function = self.func)
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
index 679efca..db93f61 100644
--- a/src/engine/SCons/Scanner/__init__.py
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -45,9 +45,17 @@ class _Null:
_null = _Null
def Scanner(function, *args, **kw):
- """Public interface factory function for creating different types
+ """
+ Public interface factory function for creating different types
of Scanners based on the different types of "functions" that may
- be supplied."""
+ be supplied.
+
+ TODO: Deprecate this some day. We've moved the functionality
+ inside the Base class and really don't need this factory function
+ any more. It was, however, used by some of our Tool modules, so
+ the call probably ended up in various people's custom modules
+ patterned on SCons code.
+ """
if SCons.Util.is_Dict(function):
return apply(Selector, (function,) + args, kw)
else:
@@ -83,7 +91,7 @@ class Base:
function,
name = "NONE",
argument = _null,
- skeys = [],
+ skeys = _null,
path_function = None,
node_class = SCons.Node.FS.Entry,
node_factory = None,
@@ -159,7 +167,14 @@ class Base:
self.path_function = path_function
self.name = name
self.argument = argument
+
+ if skeys is _null:
+ if SCons.Util.is_Dict(function):
+ skeys = function.keys()
+ else:
+ skeys = []
self.skeys = skeys
+
self.node_class = node_class
self.node_factory = node_factory
self.scan_check = scan_check
@@ -188,10 +203,13 @@ class Base:
if self.scan_check and not self.scan_check(node, env):
return []
+ self = self.select(node)
+
if not self.argument is _null:
list = self.function(node, env, path, self.argument)
else:
list = self.function(node, env, path)
+
kw = {}
if hasattr(node, 'dir'):
kw['directory'] = node.dir
@@ -221,12 +239,19 @@ class Base:
self.skeys.append(skey)
def get_skeys(self, env=None):
- if SCons.Util.is_String(self.skeys):
+ if env and SCons.Util.is_String(self.skeys):
return env.subst_list(self.skeys)[0]
return self.skeys
def select(self, node):
- return self
+ if SCons.Util.is_Dict(self.function):
+ key = node.scanner_key()
+ try:
+ return self.function[key]
+ except KeyError:
+ return None
+ else:
+ return self
def _recurse_all_nodes(self, nodes):
return nodes
@@ -236,15 +261,27 @@ class Base:
recurse_nodes = _recurse_no_nodes
+ def add_scanner(self, skey, scanner):
+ self.function[skey] = scanner
+ self.add_skey(skey)
+
class Selector(Base):
"""
A class for selecting a more specific scanner based on the
scanner_key() (suffix) for a specific Node.
+
+ TODO: This functionality has been moved into the inner workings of
+ the Base class, and this class will be deprecated at some point.
+ (It was never exposed directly as part of the public interface,
+ although it is used by the Scanner() factory function that was
+ used by various Tool modules and therefore was likely a template
+ for custom modules that may be out there.)
"""
def __init__(self, dict, *args, **kw):
apply(Base.__init__, (self, None,)+args, kw)
self.dict = dict
+ self.skeys = dict.keys()
def __call__(self, node, env, path = ()):
return self.select(node)(node, env, path)
@@ -257,6 +294,7 @@ class Selector(Base):
def add_scanner(self, skey, scanner):
self.dict[skey] = scanner
+ self.add_skey(skey)
class Current(Base):
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index 96f1526..6a0ad81 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -36,6 +36,8 @@ it goes here.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import os
import os.path
import random
@@ -62,6 +64,7 @@ import SCons.Node
import SCons.Node.FS
from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
import SCons.SConf
+import SCons.Script
import SCons.Sig
import SCons.Taskmaster
import SCons.Util
@@ -180,15 +183,8 @@ class BuildTask(SCons.Taskmaster.Task):
def postprocess(self):
if self.top:
t = self.targets[0]
- if print_tree:
- print
- SCons.Util.print_tree(t, get_all_children)
- if print_stree:
- print
- SCons.Util.print_tree(t, get_all_children, showtags=2)
- if print_dtree:
- print
- SCons.Util.print_tree(t, get_derived_children)
+ for tp in tree_printers:
+ tp.display(t)
if print_includes:
tree = t.render_include_tree()
if tree:
@@ -291,18 +287,37 @@ class QuestionTask(SCons.Taskmaster.Task):
def executed(self):
pass
+
+class TreePrinter:
+ def __init__(self, derived=False, prune=False, status=False):
+ self.derived = derived
+ self.prune = prune
+ self.status = status
+ def get_all_children(self, node):
+ return node.all_children()
+ def get_derived_children(self, node):
+ children = node.all_children(None)
+ return filter(lambda x: x.has_builder(), children)
+ def display(self, t):
+ if self.derived:
+ func = self.get_derived_children
+ else:
+ func = self.get_all_children
+ s = self.status and 2 or 0
+ SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
+
+
# Global variables
+tree_printers = []
+
keep_going_on_error = 0
-print_dtree = 0
print_explanations = 0
print_includes = 0
print_objects = 0
print_memoizer = 0
print_stacktrace = 0
-print_stree = 0
print_time = 0
-print_tree = 0
ignore_errors = 0
sconscript_time = 0
command_time = 0
@@ -390,12 +405,6 @@ memory_stats = MemStats()
# utility functions
-def get_all_children(node): return node.all_children()
-
-def get_derived_children(node):
- children = node.all_children(None)
- return filter(lambda x: x.has_builder(), children)
-
def _scons_syntax_error(e):
"""Handle syntax errors. Print out a message and show where the error
occurred.
@@ -538,10 +547,10 @@ def _SConstruct_exists(dirname=''):
def _set_globals(options):
global keep_going_on_error, ignore_errors
- global count_stats, print_dtree
+ global count_stats
global print_explanations, print_includes, print_memoizer
- global print_objects, print_stacktrace, print_stree
- global print_time, print_tree
+ global print_objects, print_stacktrace, print_time
+ global tree_printers
global memory_stats
keep_going_on_error = options.keep_going
@@ -555,7 +564,7 @@ def _set_globals(options):
if "count" in debug_values:
count_stats.enable(sys.stdout)
if "dtree" in debug_values:
- print_dtree = 1
+ tree_printers.append(TreePrinter(derived=True))
if "explain" in debug_values:
print_explanations = 1
if "findlibs" in debug_values:
@@ -573,11 +582,11 @@ def _set_globals(options):
if "stacktrace" in debug_values:
print_stacktrace = 1
if "stree" in debug_values:
- print_stree = 1
+ tree_printers.append(TreePrinter(status=True))
if "time" in debug_values:
print_time = 1
if "tree" in debug_values:
- print_tree = 1
+ tree_printers.append(TreePrinter())
ignore_errors = options.ignore_errors
def _create_path(plist):
@@ -589,6 +598,47 @@ def _create_path(plist):
path = path + '/' + d
return path
+def _load_site_scons_dir(topdir, site_dir_name=None):
+ """Load the site_scons dir under topdir.
+ Adds site_scons to sys.path, imports site_scons/site_init.py,
+ and adds site_scons/site_tools to default toolpath."""
+ if site_dir_name:
+ err_if_not_found = True # user specified: err if missing
+ else:
+ site_dir_name = "site_scons"
+ err_if_not_found = False
+
+ site_dir = os.path.join(topdir.path, site_dir_name)
+ if not os.path.exists(site_dir):
+ if err_if_not_found:
+ raise SCons.Errors.UserError, "site dir %s not found."%site_dir
+ return
+
+ site_init_filename = "site_init.py"
+ site_init_modname = "site_init"
+ site_tools_dirname = "site_tools"
+ sys.path = [site_dir] + sys.path
+ site_init_file = os.path.join(site_dir, site_init_filename)
+ site_tools_dir = os.path.join(site_dir, site_tools_dirname)
+ if os.path.exists(site_init_file):
+ import imp
+ try:
+ fp, pathname, description = imp.find_module(site_init_modname,
+ [site_dir])
+ try:
+ imp.load_module(site_init_modname, fp, pathname, description)
+ finally:
+ if fp:
+ fp.close()
+ except ImportError, e:
+ sys.stderr.write("Can't import site init file '%s': %s\n"%(site_init_file, e))
+ raise
+ except Exception, e:
+ sys.stderr.write("Site init file '%s' raised exception: %s\n"%(site_init_file, e))
+ raise
+ if os.path.exists(site_tools_dir):
+ SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir))
+
def version_string(label, module):
fmt = "\t%s: v%s.%s, %s, by %s on %s\n"
return fmt % (label,
@@ -676,7 +726,9 @@ class OptParser(OptionParser):
"pdb", "presub", "stacktrace", "stree",
"time", "tree"]
- deprecated_debug_options = [ "nomemoizer", ]
+ deprecated_debug_options = {
+ "nomemoizer" : ' and has no effect',
+ }
def opt_debug(option, opt, value, parser, debug_options=debug_options, deprecated_debug_options=deprecated_debug_options):
if value in debug_options:
@@ -686,8 +738,9 @@ class OptParser(OptionParser):
except AttributeError:
parser.values.debug = []
parser.values.debug.append(value)
- elif value in deprecated_debug_options:
- w = "The --debug=%s option is deprecated and has no effect." % value
+ elif value in deprecated_debug_options.keys():
+ msg = deprecated_debug_options[value]
+ w = "The --debug=%s option is deprecated%s." % (value, msg)
delayed_warnings.append((SCons.Warnings.DeprecatedWarning, w))
else:
raise OptionValueError("Warning: %s is not a valid debug type" % value)
@@ -773,6 +826,10 @@ class OptParser(OptionParser):
'--recon', action="store_true", dest='noexec',
default=0, help="Don't build; just print commands.")
+ self.add_option('--no-site-dir', action="store_true",
+ dest='no_site_dir', default=0,
+ help="Don't search or use the usual site_scons dir.")
+
self.add_option('--profile', action="store",
dest="profile_file", metavar="FILE",
help="Profile SCons and put results in FILE.")
@@ -790,10 +847,36 @@ class OptParser(OptionParser):
self.add_option('-s', '--silent', '--quiet', action="store_true",
default=0, help="Don't print commands.")
+ self.add_option('--site-dir', action="store",
+ dest='site_dir', metavar="DIR",
+ help="Use DIR instead of the usual site_scons dir.")
+
self.add_option('--taskmastertrace', action="store",
dest="taskmastertrace_file", metavar="FILE",
help="Trace Node evaluation to FILE.")
+ tree_options = ["all", "derived", "prune", "status"]
+
+ def opt_tree(option, opt, value, parser, tree_options=tree_options):
+ tp = TreePrinter()
+ for o in string.split(value, ','):
+ if o == 'all':
+ tp.derived = False
+ elif o == 'derived':
+ tp.derived = True
+ elif o == 'prune':
+ tp.prune = True
+ elif o == 'status':
+ tp.status = True
+ else:
+ raise OptionValueError("Warning: %s is not a valid --tree option" % o)
+ tree_printers.append(tp)
+
+ self.add_option('--tree', action="callback", type="string",
+ callback=opt_tree, nargs=1, metavar="OPTIONS",
+ help="Print a dependency tree in various formats: "
+ "%s." % string.join(tree_options, ", "))
+
self.add_option('-u', '--up', '--search-up', action="store_const",
dest="climb_up", default=0, const=1,
help="Search up directory tree for SConstruct, "
@@ -811,7 +894,8 @@ class OptParser(OptionParser):
metavar="WARNING-SPEC",
help="Enable or disable warnings.")
- self.add_option('-Y', '--repository', nargs=1, action="append",
+ self.add_option('-Y', '--repository', '--srcdir',
+ nargs=1, action="append",
help="Search REPOSITORY for source and target files.")
self.add_option('-e', '--environment-overrides', action="callback",
@@ -1076,6 +1160,11 @@ def _main(args, parser):
if options.cache_show:
fs.cache_show = 1
+ if options.site_dir:
+ _load_site_scons_dir(d, options.site_dir)
+ elif not options.no_site_dir:
+ _load_site_scons_dir(d)
+
if options.include_dir:
sys.path = options.include_dir + sys.path
@@ -1092,16 +1181,7 @@ def _main(args, parser):
SCons.Script._Add_Targets(targets)
SCons.Script._Add_Arguments(xmit_args)
- class Unbuffered:
- def __init__(self, file):
- self.file = file
- def write(self, arg):
- self.file.write(arg)
- self.file.flush()
- def __getattr__(self, attr):
- return getattr(self.file, attr)
-
- sys.stdout = Unbuffered(sys.stdout)
+ sys.stdout = SCons.Util.Unbuffered(sys.stdout)
memory_stats.append('before reading SConscript files:')
count_stats.append(('pre-', 'read'))
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index 749be6d..d25e44c 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -301,12 +301,12 @@ def SConscript_exception(file=sys.stderr):
def annotate(node):
"""Annotate a node with the stack frame describing the
SConscript file and line number that created it."""
- tb = exc_tb = sys.exc_info()[2]
+ tb = sys.exc_info()[2]
while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
tb = tb.tb_next
if not tb:
# We did not find any exec of an SConscript file: what?!
- raise InternalError, "could not find SConscript stack frame"
+ raise SCons.Errors.InternalError, "could not find SConscript stack frame"
node.creator = traceback.extract_stack(tb)[0]
# The following line would cause each Node to be annotated using the
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 067f540..35bbbf7 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -109,12 +109,12 @@ OptParser = Main.OptParser
SConscriptSettableOptions = Main.SConscriptSettableOptions
keep_going_on_error = Main.keep_going_on_error
-print_dtree = Main.print_dtree
+#print_dtree = Main.print_dtree
print_explanations = Main.print_explanations
print_includes = Main.print_includes
print_objects = Main.print_objects
print_time = Main.print_time
-print_tree = Main.print_tree
+#print_tree = Main.print_tree
memory_stats = Main.memory_stats
ignore_errors = Main.ignore_errors
#sconscript_time = Main.sconscript_time
@@ -289,6 +289,7 @@ GlobalDefaultEnvironmentFunctions = [
'Depends',
'Dir',
'NoClean',
+ 'NoCache',
'Entry',
'Execute',
'File',
@@ -341,6 +342,7 @@ GlobalDefaultBuilders = [
for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name))
+del name
# There are a handful of variables that used to live in the
# Script/SConscript.py module that some SConscript files out there were
@@ -352,6 +354,10 @@ for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
# this way by hanging some attributes off the "SConscript" object here.
SConscript = _SConscript.DefaultEnvironmentCall('SConscript')
+# Make SConscript look enough like the module it used to be so
+# that pychecker doesn't barf.
+SConscript.__name__ = 'SConscript'
+
SConscript.Arguments = ARGUMENTS
SConscript.ArgList = ARGLIST
SConscript.BuildTargets = BUILD_TARGETS
diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py
index 2a993be..989f1dd 100644
--- a/src/engine/SCons/Subst.py
+++ b/src/engine/SCons/Subst.py
@@ -442,7 +442,9 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
# This probably indicates that it's a callable
# object that doesn't match our calling arguments
# (like an Action).
- s = str(s)
+ if self.mode == SUBST_RAW:
+ return s
+ s = self.conv(s)
return self.substitute(s, lvars)
elif s is None:
return ''
@@ -646,7 +648,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
# This probably indicates that it's a callable
# object that doesn't match our calling arguments
# (like an Action).
- s = str(s)
+ if self.mode == SUBST_RAW:
+ self.append(s)
+ return
+ s = self.conv(s)
self.substitute(s, lvars, within_list)
elif s is None:
self.this_word()
diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py
index e8419f1..7ba2477 100644
--- a/src/engine/SCons/SubstTests.py
+++ b/src/engine/SCons/SubstTests.py
@@ -145,6 +145,8 @@ class SubstTestCase(unittest.TestCase):
MyNode("/bar/ack.cpp"),
MyNode("../foo/ack.c") ]
+ callable_object = TestCallable('callable-1')
+
loc = {
'xxx' : None,
'null' : '',
@@ -203,7 +205,7 @@ class SubstTestCase(unittest.TestCase):
'SSS' : '$RRR',
# Test callables that don't match the calling arguments.
- 'CALLABLE' : TestCallable('callable-1'),
+ 'CALLABLE' : callable_object,
}
env = DummyEnv(loc)
@@ -514,6 +516,16 @@ class SubstTestCase(unittest.TestCase):
else:
raise AssertionError, "did not catch expected UserError"
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary. CommandAction uses this to allow delayed evaluation
+ # of $SPAWN variables.
+ x = lambda x: x
+ r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r is callable_object, repr(r)
+ r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == 'callable-1', repr(r)
+
# Test how we handle overriding the internal conversion routines.
def s(obj):
return obj
@@ -594,6 +606,8 @@ class SubstTestCase(unittest.TestCase):
MyNode("/bar/ack.cpp"),
MyNode("../foo/ack.c") ]
+ callable_object = TestCallable('callable-2')
+
def _defines(defs):
l = []
for d in defs:
@@ -653,7 +667,7 @@ class SubstTestCase(unittest.TestCase):
'SSS' : '$RRR',
# Test callable objects that don't match our calling arguments.
- 'CALLABLE' : TestCallable('callable-2'),
+ 'CALLABLE' : callable_object,
'_defines' : _defines,
'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
@@ -792,8 +806,6 @@ class SubstTestCase(unittest.TestCase):
# Test callables that don't match our calling arguments.
'$CALLABLE', [['callable-2']],
- # Test
-
# Test handling of quotes.
# XXX Find a way to handle this in the future.
#'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
@@ -991,6 +1003,15 @@ class SubstTestCase(unittest.TestCase):
else:
raise AssertionError, "did not catch expected SyntaxError"
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary.
+ x = lambda x: x
+ r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r == [[callable_object]], repr(r)
+ r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == [['callable-2']], repr(r)
+
# Test we handle overriding the internal conversion routines.
def s(obj):
return obj
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index 04ed19a..7439168 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -41,7 +41,8 @@ interface and the SCons build engine. There are two key classes here:
which has Task subclasses that handle its specific behavior,
like printing "`foo' is up to date" when a top-level target
doesn't need to be built, and handling the -c option by removing
- targets as its "build" action.
+ targets as its "build" action. There is also a separate subclass
+ for suppressing this output when the -q option is used.
The Taskmaster instantiates a Task object for each (set of)
target(s) that it decides need to be evaluated and/or built.
@@ -58,6 +59,8 @@ import SCons.Errors
StateString = SCons.Node.StateString
+
+
# A subsystem for recording stats about how different Nodes are handled by
# the main Taskmaster loop. There's no external control here (no need for
# a --debug= option); enable it by changing the value of CollectStats.
@@ -68,7 +71,7 @@ class Stats:
"""
A simple class for holding statistics about the disposition of a
Node by the Taskmaster. If we're collecting statistics, each Node
- processed by the Taskmaster gets one of these attached, in which
+ processed by the Taskmaster gets one of these attached, in which case
the Taskmaster records its decision each time it processes the Node.
(Ideally, that's just once per Node.)
"""
@@ -100,8 +103,11 @@ def dump_stats():
for n in StatsNodes:
print (fmt % n.stats.__dict__) + str(n)
+
+
class Task:
- """Default SCons build engine task.
+ """
+ Default SCons build engine task.
This controls the interaction of the actual building of node
and the rest of the engine.
@@ -116,7 +122,8 @@ class Task:
Note that it's generally a good idea for sub-classes to call
these methods explicitly to update state, etc., rather than
- roll their own interaction with Taskmaster from scratch."""
+ roll their own interaction with Taskmaster from scratch.
+ """
def __init__(self, tm, targets, top, node):
self.tm = tm
self.targets = targets
@@ -125,15 +132,26 @@ class Task:
self.exc_clear()
def display(self, message):
- """Allow the calling interface to display a message
+ """
+ Hook to allow the calling interface to display a message.
+
+ This hook gets called as part of preparing a task for execution
+ (that is, a Node to be built). As part of figuring out what Node
+ should be built next, the actually target list may be altered,
+ along with a message describing the alteration. The calling
+ interface can subclass Task and provide a concrete implementation
+ of this method to see those messages.
"""
pass
def prepare(self):
- """Called just before the task is executed.
+ """
+ Called just before the task is executed.
- This unlinks all targets and makes all directories before
- building anything."""
+ This is mainly intended to give the target Nodes a chance to
+ unlink underlying files and make all necessary directories before
+ the Action is actually called to build the targets.
+ """
# Now that it's the appropriate time, give the TaskMaster a
# chance to raise any exceptions it encountered while preparing
@@ -155,11 +173,13 @@ class Task:
return self.node
def execute(self):
- """Called to execute the task.
+ """
+ Called to execute the task.
This method is called from multiple threads in a parallel build,
so only do thread safe stuff here. Do thread unsafe stuff in
- prepare(), executed() or failed()."""
+ prepare(), executed() or failed().
+ """
try:
everything_was_cached = 1
@@ -183,13 +203,13 @@ class Task:
sys.exc_info())
def executed(self):
- """Called when the task has been successfully executed.
+ """
+ Called when the task has been successfully executed.
- This may have been a do-nothing operation (to preserve
- build order), so check the node's state before updating
- things. Most importantly, this calls back to the
- Taskmaster to put any node tasks waiting on this one
- back on the pending list."""
+ This may have been a do-nothing operation (to preserve build
+ order), so we have to check the node's state before deciding
+ whether it was "built" or just "visited."
+ """
for t in self.targets:
if t.get_state() == SCons.Node.executing:
t.set_state(SCons.Node.executed)
@@ -197,17 +217,18 @@ class Task:
else:
t.visited()
- self.tm.executed(self.node)
-
def failed(self):
- """Default action when a task fails: stop the build."""
+ """
+ Default action when a task fails: stop the build.
+ """
self.fail_stop()
def fail_stop(self):
- """Explicit stop-the-build failure."""
+ """
+ Explicit stop-the-build failure.
+ """
for t in self.targets:
t.set_state(SCons.Node.failed)
- self.tm.failed(self.node)
self.tm.stop()
# We're stopping because of a build failure, but give the
@@ -217,7 +238,8 @@ class Task:
self.top = 1
def fail_continue(self):
- """Explicit continue-the-build failure.
+ """
+ Explicit continue-the-build failure.
This sets failure status on the target nodes and all of
their dependent parent nodes.
@@ -228,10 +250,9 @@ class Task:
def set_state(node): node.set_state(SCons.Node.failed)
t.call_for_all_waiting_parents(set_state)
- self.tm.executed(self.node)
-
def make_ready_all(self):
- """Mark all targets in a task ready for execution.
+ """
+ Marks all targets in a task ready for execution.
This is used when the interface needs every target Node to be
visited--the canonical example being the "scons -c" option.
@@ -243,7 +264,8 @@ class Task:
s.set_state(SCons.Node.executing)
def make_ready_current(self):
- """Mark all targets in a task ready for execution if any target
+ """
+ Marks all targets in a task ready for execution if any target
is not current.
This is the default behavior for building only what's necessary.
@@ -261,11 +283,28 @@ class Task:
make_ready = make_ready_current
def postprocess(self):
- """Post process a task after it's been executed."""
+ """
+ Post-processes a task after it's been executed.
+
+ This examines all the targets just built (or not, we don't care
+ if the build was successful, or even if there was no build
+ because everything was up-to-date) to see if they have any
+ waiting parent Nodes, or Nodes waiting on a common side effect,
+ that can be put back on the candidates list.
+ """
+
+ # We may have built multiple targets, some of which may have
+ # common parents waiting for this build. Count up how many
+ # targets each parent was waiting for so we can subtract the
+ # values later, and so we *don't* put waiting side-effect Nodes
+ # back on the candidates list if the Node is also a waiting
+ # parent.
+
parents = {}
for t in self.targets:
for p in t.waiting_parents.keys():
parents[p] = parents.get(p, 0) + 1
+
for t in self.targets:
for s in t.side_effects:
if s.get_state() == SCons.Node.executing:
@@ -276,21 +315,47 @@ class Task:
for p in s.waiting_s_e.keys():
if p.ref_count == 0:
self.tm.candidates.append(p)
+
for p, subtract in parents.items():
p.ref_count = p.ref_count - subtract
if p.ref_count == 0:
self.tm.candidates.append(p)
+
for t in self.targets:
t.postprocess()
+ # Exception handling subsystem.
+ #
+ # Exceptions that occur while walking the DAG or examining Nodes
+ # must be raised, but must be raised at an appropriate time and in
+ # a controlled manner so we can, if necessary, recover gracefully,
+ # possibly write out signature information for Nodes we've updated,
+ # etc. This is done by having the Taskmaster tell us about the
+ # exception, and letting
+
def exc_info(self):
+ """
+ Returns info about a recorded exception.
+ """
return self.exception
def exc_clear(self):
+ """
+ Clears any recorded exception.
+
+ This also changes the "exception_raise" attribute to point
+ to the appropriate do-nothing method.
+ """
self.exception = (None, None, None)
self.exception_raise = self._no_exception_to_raise
def exception_set(self, exception=None):
+ """
+ Records an exception to be raised at the appropriate time.
+
+ This also changes the "exception_raise" attribute to point
+ to the method that will, in fact
+ """
if not exception:
exception = sys.exc_info()
self.exception = exception
@@ -300,14 +365,17 @@ class Task:
pass
def _exception_raise(self):
- """Raise a pending exception that was recorded while
- getting a Task ready for execution."""
- self.tm.exception_raise(self.exc_info())
-
-
-def order(dependencies):
- """Re-order a list of dependencies (if we need to)."""
- return dependencies
+ """
+ Raises a pending exception that was recorded while getting a
+ Task ready for execution.
+ """
+ exc = self.exc_info()[:]
+ try:
+ exc_type, exc_value, exc_traceback = exc
+ except ValueError:
+ exc_type, exc_value = exc
+ exc_traceback = None
+ raise exc_type, exc_value, exc_traceback
def find_cycle(stack):
@@ -322,24 +390,41 @@ def find_cycle(stack):
class Taskmaster:
- """A generic Taskmaster for handling a bunch of targets.
-
- Classes that override methods of this class should call
- the base class method, so this class can do its thing.
+ """
+ The Taskmaster for walking the dependency DAG.
"""
- def __init__(self, targets=[], tasker=Task, order=order, trace=None):
+ def __init__(self, targets=[], tasker=Task, order=None, trace=None):
self.top_targets = targets[:]
self.top_targets.reverse()
self.candidates = []
self.tasker = tasker
- self.ready = None # the next task that is ready to be executed
+ if not order:
+ order = lambda l: l
self.order = order
self.message = None
self.trace = trace
self.next_candidate = self.find_next_candidate
def find_next_candidate(self):
+ """
+ Returns the next candidate Node for (potential) evaluation.
+
+ The candidate list (really a stack) initially consists of all of
+ the top-level (command line) targets provided when the Taskmaster
+ was initialized. While we walk the DAG, visiting Nodes, all the
+ children that haven't finished processing get pushed on to the
+ candidate list. Each child can then be popped and examined in
+ turn for whether *their* children are all up-to-date, in which
+ case a Task will be created for their actual evaluation and
+ potential building.
+
+ Here is where we also allow candidate Nodes to alter the list of
+ Nodes that should be examined. This is used, for example, when
+ invoking SCons in a source directory. A source directory Node can
+ return its corresponding build directory Node, essentially saying,
+ "Hey, you really need to build this thing over here instead."
+ """
try:
return self.candidates.pop()
except IndexError:
@@ -358,13 +443,32 @@ class Taskmaster:
return node
def no_next_candidate(self):
+ """
+ Stops Taskmaster processing by not returning a next candidate.
+ """
return None
def _find_next_ready_node(self):
- """Find the next node that is ready to be built"""
-
- if self.ready:
- return
+ """
+ Finds the next node that is ready to be built.
+
+ This is *the* main guts of the DAG walk. We loop through the
+ list of candidates, looking for something that has no un-built
+ children (i.e., that is a leaf Node or has dependencies that are
+ all leaf Nodes or up-to-date). Candidate Nodes are re-scanned
+ (both the target Node itself and its sources, which are always
+ scanned in the context of a given target) to discover implicit
+ dependencies. A Node that must wait for some children to be
+ built will be put back on the candidates list after the children
+ have finished building. A Node that has been put back on the
+ candidates list in this way may have itself (or its sources)
+ re-scanned, in order to handle generated header files (e.g.) and
+ the implicit dependencies therein.
+
+ Note that this method does not do any signature calculation or
+ up-to-date check itself. All of that is handled by the Task
+ class. This is purely concerned with the dependency graph walk.
+ """
self.ready_exc = None
@@ -373,8 +477,7 @@ class Taskmaster:
while 1:
node = self.next_candidate()
if node is None:
- self.ready = None
- break
+ return None
node = node.disambiguate()
state = node.get_state()
@@ -405,9 +508,8 @@ class Taskmaster:
exc_value = sys.exc_info()[1]
e = SCons.Errors.ExplicitExit(node, exc_value.code)
self.ready_exc = (SCons.Errors.ExplicitExit, e)
- self.ready = node
if T: T.write(' SystemExit\n')
- break
+ return node
except KeyboardInterrupt:
if T: T.write(' KeyboardInterrupt\n')
raise
@@ -417,10 +519,9 @@ class Taskmaster:
# BuildDir, or a Scanner threw something). Arrange to
# raise the exception when the Task is "executed."
self.ready_exc = sys.exc_info()
- self.ready = node
if S: S.problem = S.problem + 1
if T: T.write(' exception\n')
- break
+ return node
if T and children:
c = map(str, children)
@@ -516,7 +617,7 @@ class Taskmaster:
continue
# Skip this node if it has side-effects that are currently being
- # built themselves or waiting for something else being built.
+ # built themselves or waiting for something else being built.
side_effects = filter(lambda N:
N.get_state() == SCons.Node.executing,
node.side_effects)
@@ -531,17 +632,20 @@ class Taskmaster:
# The default when we've gotten through all of the checks above:
# this node is ready to be built.
- self.ready = node
if S: S.build = S.build + 1
if T: T.write(' evaluating %s\n' % node)
- break
+ return node
- def next_task(self):
- """Return the next task to be executed."""
+ return None
- self._find_next_ready_node()
+ def next_task(self):
+ """
+ Returns the next task to be executed.
- node = self.ready
+ This simply asks for the next Node to be evaluated, and then wraps
+ it in the specific Task subclass with which we were initialized.
+ """
+ node = self._find_next_ready_node()
if node is None:
return None
@@ -563,27 +667,12 @@ class Taskmaster:
if self.ready_exc:
task.exception_set(self.ready_exc)
- self.ready = None
self.ready_exc = None
return task
def stop(self):
- """Stop the current build completely."""
+ """
+ Stops the current build completely.
+ """
self.next_candidate = self.no_next_candidate
- self.ready = None
-
- def failed(self, node):
- pass
-
- def executed(self, node):
- pass
-
- def exception_raise(self, exception):
- exc = exception[:]
- try:
- exc_type, exc_value, exc_traceback = exc
- except ValueError:
- exc_type, exc_value = exc
- exc_traceback = None
- raise exc_type, exc_value, exc_traceback
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index 1803eee..f74cf34 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -681,16 +681,6 @@ class TaskmasterTestCase(unittest.TestCase):
assert built_text == "MyTM.stop()"
assert tm.next_task() is None
- def test_failed(self):
- """Test when a task has failed
- """
- n1 = Node("n1")
- tm = SCons.Taskmaster.Taskmaster([n1])
- t = tm.next_task()
- assert t.targets == [n1], map(str, t.targets)
- tm.failed(n1)
- assert t.targets == [n1], map(str, t.targets)
-
def test_executed(self):
"""Test when a task has been executed
"""
@@ -974,20 +964,6 @@ class TaskmasterTestCase(unittest.TestCase):
else:
assert 0, "did not catch expected exception"
- t.exception_set(("exception 4", "XYZZY"))
- def fw_exc(exc):
- raise 'exception_forwarded', exc
- tm.exception_raise = fw_exc
- try:
- t.exception_raise()
- except:
- exc_type, exc_value = sys.exc_info()[:2]
- assert exc_type == 'exception_forwarded', exc_type
- assert exc_value[0] == "exception 4", exc_value[0]
- assert exc_value[1] == "XYZZY", exc_value[1]
- else:
- assert 0, "did not catch expected exception"
-
def test_postprocess(self):
"""Test postprocessing targets to give them a chance to clean up
"""
diff --git a/src/engine/SCons/Tool/BitKeeper.py b/src/engine/SCons/Tool/BitKeeper.py
index 2c2bfdd..b6561f4 100644
--- a/src/engine/SCons/Tool/BitKeeper.py
+++ b/src/engine/SCons/Tool/BitKeeper.py
@@ -34,8 +34,6 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import os.path
-
import SCons.Action
import SCons.Builder
import SCons.Util
diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py
index 77363f0..09ce1d1 100644
--- a/src/engine/SCons/Tool/JavaCommon.py
+++ b/src/engine/SCons/Tool/JavaCommon.py
@@ -148,8 +148,10 @@ if java_parsing:
self.outer_state = outer_state
self.tokens_to_find = 2
def parseToken(self, token):
- # This is an anonymous class if and only if the next token
- # is a bracket
+ # This is an anonymous class if and only if the next
+ # non-whitespace token is a bracket
+ if token == '\n':
+ return self
if token == '{':
self.outer_state.addAnonClass()
elif token in ['"', "'"]:
diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py
index 6d9fc43..4a7f9cf 100644
--- a/src/engine/SCons/Tool/JavaCommonTests.py
+++ b/src/engine/SCons/Tool/JavaCommonTests.py
@@ -265,6 +265,27 @@ public enum a {}
assert classes == ['a'], classes
+ def test_anon_classes(self):
+ """Test anonymous classes"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+public abstract class TestClass
+{
+ public void completed()
+ {
+ new Thread()
+ {
+ }.start();
+
+ new Thread()
+ {
+ }.start();
+ }
+}
+""")
+ assert pkg_dir == None, pkg_dir
+ assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes
+
+
if __name__ == "__main__":
suite = unittest.TestSuite()
diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py
index 5ddac19..52c032f 100644
--- a/src/engine/SCons/Tool/ToolTests.py
+++ b/src/engine/SCons/Tool/ToolTests.py
@@ -45,6 +45,8 @@ class ToolTestCase(unittest.TestCase):
return self.dict[key]
def __setitem__(self, key, val):
self.dict[key] = val
+ def has_key(self, key):
+ return self.dict.has_key(key)
env = Environment()
env['BUILDERS'] = {}
env['ENV'] = {}
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index d36478d..b2e2eff 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -41,6 +41,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import imp
import sys
+import SCons.Builder
import SCons.Errors
import SCons.Scanner
import SCons.Scanner.C
@@ -48,11 +49,13 @@ import SCons.Scanner.D
import SCons.Scanner.LaTeX
import SCons.Scanner.Prog
+DefaultToolpath=[]
+
CScanner = SCons.Scanner.C.CScanner()
DScanner = SCons.Scanner.D.DScanner()
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
-SourceFileScanner = SCons.Scanner.Scanner({}, name='SourceFileScanner')
+SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
".h", ".H", ".hxx", ".hpp", ".hh",
@@ -78,7 +81,7 @@ for suffix in LaTeXSuffixes:
class Tool:
def __init__(self, name, toolpath=[], **kw):
self.name = name
- self.toolpath = toolpath
+ self.toolpath = toolpath + DefaultToolpath
# remember these so we can merge them into the call
self.init_kw = kw
diff --git a/src/engine/SCons/Tool/c++.py b/src/engine/SCons/Tool/c++.py
index a44fa6d..d9370b0 100644
--- a/src/engine/SCons/Tool/c++.py
+++ b/src/engine/SCons/Tool/c++.py
@@ -60,6 +60,8 @@ def generate(env):
Add Builders and construction variables for Visual Age C++ compilers
to an Environment.
"""
+ import SCons.Tool
+ import SCons.Tool.cc
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in CXXSuffixes:
@@ -67,6 +69,8 @@ def generate(env):
shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ SCons.Tool.cc.add_common_cc_variables(env)
env['CXX'] = 'c++'
env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py
index 62b945f..d1a287a 100644
--- a/src/engine/SCons/Tool/cc.py
+++ b/src/engine/SCons/Tool/cc.py
@@ -40,6 +40,28 @@ CSuffixes = ['.c', '.m']
if not SCons.Util.case_sensitive_suffixes('.c', '.C'):
CSuffixes.append('.C')
+def add_common_cc_variables(env):
+ """
+ Add underlying common "C compiler" variables that
+ are used by multiple tools (specifically, c++).
+ """
+ if not env.has_key('_CCCOMCOM'):
+ env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
+ # It's a hack to test for darwin here, but the alternative
+ # of creating an applecc.py to contain this seems overkill.
+ # Maybe someday the Apple platform will require more setup and
+ # this logic will be moved.
+ env['FRAMEWORKS'] = SCons.Util.CLVar('')
+ env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
+ if env['PLATFORM'] == 'darwin':
+ env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+
+ if not env.has_key('CCFLAGS'):
+ env['CCFLAGS'] = SCons.Util.CLVar('')
+
+ if not env.has_key('SHCCFLAGS'):
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+
def generate(env):
"""
Add Builders and construction variables for C compilers to an Environment.
@@ -51,22 +73,13 @@ def generate(env):
shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
-
- env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
- # It's a hack to test for darwin here, but the alternative of creating
- # an applecc.py to contain this seems overkill. Maybe someday the Apple
- # platform will require more setup and this logic will be moved.
- env['FRAMEWORKS'] = SCons.Util.CLVar('')
- env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
- if env['PLATFORM'] == 'darwin':
- env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+
+ add_common_cc_variables(env)
env['CC'] = 'cc'
- env['CCFLAGS'] = SCons.Util.CLVar('')
env['CFLAGS'] = SCons.Util.CLVar('')
env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
env['SHCC'] = '$CC'
- env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
diff --git a/src/engine/SCons/Tool/cvf.py b/src/engine/SCons/Tool/cvf.py
index 28a1915..4bca52b 100644
--- a/src/engine/SCons/Tool/cvf.py
+++ b/src/engine/SCons/Tool/cvf.py
@@ -29,7 +29,6 @@ Tool-specific initialization for the Compaq Visual Fortran compiler.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import SCons.Util
import fortran
compilers = ['f90']
diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py
index cae264b..64ffb68 100644
--- a/src/engine/SCons/Tool/dmd.py
+++ b/src/engine/SCons/Tool/dmd.py
@@ -201,6 +201,8 @@ def generate(env):
env.Append(LIBS = ['phobos'])
if 'pthread' not in libs:
env.Append(LIBS = ['pthread'])
+ if 'm' not in libs:
+ env.Append(LIBS = ['m'])
return defaultLinker
env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink
diff --git a/src/engine/SCons/Tool/dvi.py b/src/engine/SCons/Tool/dvi.py
index fce9850..78fb4c2 100644
--- a/src/engine/SCons/Tool/dvi.py
+++ b/src/engine/SCons/Tool/dvi.py
@@ -36,7 +36,7 @@ DVIBuilder = None
def generate(env):
try:
- bld = env['BUILDERS']['DVI']
+ env['BUILDERS']['DVI']
except KeyError:
global DVIBuilder
diff --git a/src/engine/SCons/Tool/f77.py b/src/engine/SCons/Tool/f77.py
index 75c3c2d..bd1e870 100644
--- a/src/engine/SCons/Tool/f77.py
+++ b/src/engine/SCons/Tool/f77.py
@@ -54,6 +54,7 @@ F77Scan = SCons.Scanner.Fortran.FortranScan("F77PATH")
for suffix in F77Suffixes + F77PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F77Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py
index cb450b6..fab4ccb 100644
--- a/src/engine/SCons/Tool/f90.py
+++ b/src/engine/SCons/Tool/f90.py
@@ -54,6 +54,7 @@ F90Scan = SCons.Scanner.Fortran.FortranScan("F90PATH")
for suffix in F90Suffixes + F90PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F90Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py
index 7adc80b..94786c8 100644
--- a/src/engine/SCons/Tool/f95.py
+++ b/src/engine/SCons/Tool/f95.py
@@ -53,6 +53,7 @@ F95Scan = SCons.Scanner.Fortran.FortranScan("F95PATH")
for suffix in F95Suffixes + F95PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F95Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
diff --git a/src/engine/SCons/Tool/fortran.py b/src/engine/SCons/Tool/fortran.py
index b83e7d3..8494fd6 100644
--- a/src/engine/SCons/Tool/fortran.py
+++ b/src/engine/SCons/Tool/fortran.py
@@ -63,6 +63,7 @@ FortranScan = SCons.Scanner.Fortran.FortranScan("FORTRANPATH")
for suffix in FortranSuffixes + FortranPPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, FortranScan)
+del suffix
#
def _fortranEmitter(target, source, env):
diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py
index e668bf0..d95a4be 100644
--- a/src/engine/SCons/Tool/intelc.py
+++ b/src/engine/SCons/Tool/intelc.py
@@ -151,7 +151,7 @@ def get_intel_registry_value(valuename, version=None, abi=None):
return v # or v.encode('iso-8859-1', 'replace') to remove unicode?
except SCons.Util.RegError:
raise MissingRegistryError, \
- "%s\\%s was not found in the registry."%(K, value)
+ "%s\\%s was not found in the registry."%(K, valuename)
def get_all_compiler_versions():
diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py
index ed93412..cb0a8eb 100644
--- a/src/engine/SCons/Tool/jar.py
+++ b/src/engine/SCons/Tool/jar.py
@@ -35,11 +35,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Action
import SCons.Builder
+import SCons.Subst
import SCons.Util
def jarSources(target, source, env, for_signature):
"""Only include sources that are not a manifest file."""
jarchdir = env.subst('$JARCHDIR')
+ if jarchdir:
+ jarchdir = env.fs.Dir(jarchdir)
result = []
for src in source:
contents = src.get_contents()
@@ -47,7 +50,7 @@ def jarSources(target, source, env, for_signature):
if jarchdir:
# If we are changing the dir with -C, then sources should
# be relative to that directory.
- src = src.get_path(src.fs.Dir(jarchdir))
+ src = SCons.Subst.Literal(src.get_path(jarchdir))
result.append('-C')
result.append(jarchdir)
result.append(src)
diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py
index 0639535..cc7f584 100644
--- a/src/engine/SCons/Tool/mingw.py
+++ b/src/engine/SCons/Tool/mingw.py
@@ -39,6 +39,7 @@ import string
import SCons.Action
import SCons.Builder
+import SCons.Defaults
import SCons.Tool
import SCons.Util
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
index e35c92a..b84b277 100644
--- a/src/engine/SCons/Tool/msvs.py
+++ b/src/engine/SCons/Tool/msvs.py
@@ -205,7 +205,7 @@ class _DSPGenerator:
if len(buildtarget) == 1:
bt = buildtarget[0]
buildtarget = []
- for v in variants:
+ for _ in variants:
buildtarget.append(bt)
if not env.has_key('outdir') or env['outdir'] == None:
@@ -1010,7 +1010,6 @@ class _GenerateV7DSW(_DSWGenerator):
self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
for name in confkeys:
- name = name
variant = self.configs[name].variant
platform = self.configs[name].platform
if self.version_num >= 8.0:
@@ -1279,7 +1278,7 @@ def get_visualstudio8_suites():
try:
idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
r'Software\Microsoft\VisualStudio\8.0')
- id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
+ SCons.Util.RegQueryValueEx(idk, 'InstallDir')
editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions
edition_name = 'STD'
for name, key_suffix in editions.items():
@@ -1297,7 +1296,7 @@ def get_visualstudio8_suites():
try:
idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
r'Software\Microsoft\VCExpress\8.0')
- id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
+ SCons.Util.RegQueryValueEx(idk, 'InstallDir')
suites.append('EXPRESS')
except SCons.Util.RegError:
pass
diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py
index a1ede44..0d5ce82 100644
--- a/src/engine/SCons/Tool/mwcc.py
+++ b/src/engine/SCons/Tool/mwcc.py
@@ -32,11 +32,11 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import SCons.Util
import os
import os.path
import string
+import SCons.Util
def set_vars(env):
"""Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars
@@ -155,6 +155,8 @@ CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
def generate(env):
"""Add Builders and construction variables for the mwcc to an Environment."""
+ import SCons.Defaults
+ import SCons.Tool
set_vars(env)
diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py
index e2b1827..e73c730 100644
--- a/src/engine/SCons/Tool/mwld.py
+++ b/src/engine/SCons/Tool/mwld.py
@@ -33,7 +33,6 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Tool
-import SCons.Tool.mwcc
def generate(env):
@@ -60,7 +59,8 @@ def generate(env):
def exists(env):
- return mwcc.set_versions(env)
+ import SCons.Tool.mwcc
+ return SCons.Tool.mwcc.set_vars(env)
def shlib_generator(target, source, env, for_signature):
diff --git a/src/engine/SCons/Tool/pdf.py b/src/engine/SCons/Tool/pdf.py
index 0f6468b..b0cd126 100644
--- a/src/engine/SCons/Tool/pdf.py
+++ b/src/engine/SCons/Tool/pdf.py
@@ -36,7 +36,7 @@ PDFBuilder = None
def generate(env):
try:
- bld = env['BUILDERS']['PDF']
+ env['BUILDERS']['PDF']
except KeyError:
global PDFBuilder
if PDFBuilder is None:
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
index 4d290c7..105f42e 100644
--- a/src/engine/SCons/Tool/qt.py
+++ b/src/engine/SCons/Tool/qt.py
@@ -75,7 +75,6 @@ def checkMocIncluded(target, source, env):
(str(moc), str(cpp)))
def find_file(filename, paths, node_factory):
- retval = None
for dir in paths:
node = node_factory(filename, dir)
if node.rexists():
@@ -219,7 +218,6 @@ def uicEmitter(target, source, env):
return target, source
def uicScannerFunc(node, env, path):
- dir = node.dir
lookout = []
lookout.extend(env['CPPPATH'])
lookout.append(str(node.rfile().dir))
@@ -231,18 +229,17 @@ def uicScannerFunc(node, env, path):
result.append(dep)
return result
-uicScanner = SCons.Scanner.Scanner(uicScannerFunc,
- name = "UicScanner",
- node_class = SCons.Node.FS.File,
- node_factory = SCons.Node.FS.File,
- recursive = 0)
+uicScanner = SCons.Scanner.Base(uicScannerFunc,
+ name = "UicScanner",
+ node_class = SCons.Node.FS.File,
+ node_factory = SCons.Node.FS.File,
+ recursive = 0)
def generate(env):
"""Add Builders and construction variables for qt to an Environment."""
CLVar = SCons.Util.CLVar
Action = SCons.Action.Action
Builder = SCons.Builder.Builder
- splitext = SCons.Util.splitext
env.SetDefault(QTDIR = _detect(env),
QT_BINPATH = os.path.join('$QTDIR', 'bin'),
diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py
index a8e12a2..04c3b2a 100644
--- a/src/engine/SCons/Tool/swig.py
+++ b/src/engine/SCons/Tool/swig.py
@@ -77,7 +77,6 @@ def recurse(path, searchPath):
return found
def _scanSwig(node, env, path):
- import sys
r = recurse(str(node), [os.path.abspath(os.path.dirname(str(node))), os.path.abspath(os.path.join("include", "swig"))])
return r
@@ -85,7 +84,8 @@ def _swigEmitter(target, source, env):
for src in source:
src = str(src)
mname = None
- if "-python" in SCons.Util.CLVar(env.subst("$SWIGFLAGS")):
+ flags = SCons.Util.CLVar(env.subst("$SWIGFLAGS"))
+ if "-python" in flags and "-noproxy" not in flags:
f = open(src)
try:
for l in f.readlines():
diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py
index 0329667..1defd90 100644
--- a/src/engine/SCons/Tool/tex.py
+++ b/src/engine/SCons/Tool/tex.py
@@ -112,7 +112,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
# Now decide if latex needs to be run yet again.
logfilename = basename + '.log'
- for trial in range(int(env.subst('$LATEXRETRIES'))):
+ for _ in range(int(env.subst('$LATEXRETRIES'))):
if not os.path.exists(logfilename):
break
content = open(logfilename, "rb").read()
diff --git a/src/engine/SCons/Tool/zip.py b/src/engine/SCons/Tool/zip.py
index f0d4ed0..22f63fb 100644
--- a/src/engine/SCons/Tool/zip.py
+++ b/src/engine/SCons/Tool/zip.py
@@ -36,6 +36,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
import SCons.Builder
+import SCons.Defaults
import SCons.Node.FS
import SCons.Util
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 4bd050a..eb38e44 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -37,7 +37,6 @@ import os.path
import re
import string
import sys
-import stat
import types
from UserDict import UserDict
@@ -234,9 +233,6 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}):
rname = str(root)
- if visited.has_key(rname):
- return ""
-
children = child_func(root)
retval = ""
for pipe in margin[:-1]:
@@ -245,6 +241,9 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}):
else:
retval = retval + " "
+ if visited.has_key(rname):
+ return retval + "+-[" + rname + "]\n"
+
retval = retval + "+-" + rname + "\n"
if not prune:
visited = copy.copy(visited)
@@ -278,21 +277,19 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
rname = str(root)
- if visited.has_key(rname):
- return
-
if showtags:
if showtags == 2:
- print ' E = exists'
- print ' R = exists in repository only'
- print ' b = implicit builder'
- print ' B = explicit builder'
- print ' S = side effect'
- print ' P = precious'
- print ' A = always build'
- print ' C = current'
- print ' N = no clean'
+ print ' E = exists'
+ print ' R = exists in repository only'
+ print ' b = implicit builder'
+ print ' B = explicit builder'
+ print ' S = side effect'
+ print ' P = precious'
+ print ' A = always build'
+ print ' C = current'
+ print ' N = no clean'
+ print ' H = no cache'
print ''
tags = ['[']
@@ -305,6 +302,7 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
tags.append(' A'[IDX(root.always_build)])
tags.append(' C'[IDX(root.current())])
tags.append(' N'[IDX(root.noclean)])
+ tags.append(' H'[IDX(root.nocache)])
tags.append(']')
else:
@@ -314,6 +312,10 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
return [" ","| "][m]
margins = map(MMM, margin[:-1])
+ if visited.has_key(rname):
+ print string.join(tags + margins + ['+-[', rname, ']'], '')
+ return
+
print string.join(tags + margins + ['+-', rname], '')
if prune:
@@ -567,6 +569,7 @@ elif os.name == 'os2':
else:
def WhereIs(file, path=None, pathext=None, reject=[]):
+ import stat
if path is None:
try:
path = os.environ['PATH']
@@ -864,9 +867,10 @@ def unique(s):
for x in s:
u[x] = 1
except TypeError:
- del u # move on to the next method
+ pass # move on to the next method
else:
return u.keys()
+ del u
# We can't hash all the elements. Second fastest is to sort,
# which brings the equal elements together; then duplicates are
@@ -879,7 +883,7 @@ def unique(s):
t = list(s)
t.sort()
except TypeError:
- del t # move on to the next method
+ pass # move on to the next method
else:
assert n > 0
last = t[0]
@@ -890,6 +894,7 @@ def unique(s):
lasti = lasti + 1
i = i + 1
return t[:lasti]
+ del t
# Brute force is all that's left.
u = []
@@ -926,3 +931,16 @@ class LogicalLines:
break
result.append(line)
return result
+
+class Unbuffered:
+ """
+ A proxy class that wraps a file object, flushing after every write,
+ and delegating everything else to the wrapped object.
+ """
+ def __init__(self, file):
+ self.file = file
+ def write(self, arg):
+ self.file.write(arg)
+ self.file.flush()
+ def __getattr__(self, attr):
+ return getattr(self.file, attr)
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index e291662..db6d9f6 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -56,6 +56,7 @@ class UtilTestCase(unittest.TestCase):
def __init__(self, name, children=[]):
self.children = children
self.name = name
+ self.nocache = None
def __str__(self):
return self.name
def exists(self):
@@ -100,7 +101,7 @@ class UtilTestCase(unittest.TestCase):
"""
lines = string.split(expect, '\n')[:-1]
- lines = map(lambda l: '[E BSPACN]'+l, lines)
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
withtags = string.join(lines, '\n') + '\n'
return foo, expect, withtags
@@ -120,10 +121,11 @@ class UtilTestCase(unittest.TestCase):
+-blat.h
| +-stdlib.h
+-bar.h
+ +-[stdlib.h]
"""
lines = string.split(expect, '\n')[:-1]
- lines = map(lambda l: '[E BSPACN]'+l, lines)
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
withtags = string.join(lines, '\n') + '\n'
return blat_o, expect, withtags
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
index 1b13c96..b313ee0 100644
--- a/src/engine/SCons/Warnings.py
+++ b/src/engine/SCons/Warnings.py
@@ -35,6 +35,7 @@ class Warning(SCons.Errors.UserError):
pass
+# NOTE: If you add a new warning class, add it to the man page, too!
class CacheWriteErrorWarning(Warning):
pass
@@ -51,6 +52,9 @@ class DeprecatedWarning(Warning):
class DuplicateEnvironmentWarning(Warning):
pass
+class MisleadingKeywordsWarning(Warning):
+ pass
+
class MissingSConscriptWarning(Warning):
pass
@@ -66,9 +70,6 @@ class NoParallelSupportWarning(Warning):
class ReservedVariableWarning(Warning):
pass
-class MisleadingKeywordsWarning(Warning):
- pass
-
_warningAsException = 0
# The below is a list of 2-tuples. The first element is a class object.
diff --git a/src/engine/SCons/__init__.py b/src/engine/SCons/__init__.py
index 20e4734..b548841 100644
--- a/src/engine/SCons/__init__.py
+++ b/src/engine/SCons/__init__.py
@@ -38,5 +38,3 @@ __buildsys__ = "__BUILDSYS__"
__date__ = "__DATE__"
__developer__ = "__DEVELOPER__"
-
-import SCons.Memoize
diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py
index 6112db5..7b74ab4 100644
--- a/src/engine/SCons/compat/__init__.py
+++ b/src/engine/SCons/compat/__init__.py
@@ -80,6 +80,25 @@ def import_as(module, name):
import builtins
try:
+ set
+except NameError:
+ # Pre-2.4 Python has no native set type
+ try:
+ # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module
+ # that we grabbed.
+ import_as('_sets', 'sets')
+ except (ImportError, SyntaxError):
+ # Python 1.5 (ImportError, no __future_ module) and 2.1
+ # (SyntaxError, no generators in __future__) will blow up
+ # trying to import the 2.[45] sets module, so back off to a
+ # custom sets module that can be discarded easily when we
+ # stop supporting those versions.
+ import_as('_sets15', 'sets')
+ import __builtin__
+ import sets
+ __builtin__.set = sets.Set
+
+try:
import subprocess
except ImportError:
# Pre-2.4 Python has no subprocess module.
diff --git a/src/engine/SCons/compat/_sets.py b/src/engine/SCons/compat/_sets.py
new file mode 100644
index 0000000..32a0dd6
--- /dev/null
+++ b/src/engine/SCons/compat/_sets.py
@@ -0,0 +1,577 @@
+"""Classes to represent arbitrary sets (including sets of sets).
+
+This module implements sets using dictionaries whose values are
+ignored. The usual operations (union, intersection, deletion, etc.)
+are provided as both methods and operators.
+
+Important: sets are not sequences! While they support 'x in s',
+'len(s)', and 'for x in s', none of those operations are unique for
+sequences; for example, mappings support all three as well. The
+characteristic operation for sequences is subscripting with small
+integers: s[i], for i in range(len(s)). Sets don't support
+subscripting at all. Also, sequences allow multiple occurrences and
+their elements have a definite order; sets on the other hand don't
+record multiple occurrences and don't remember the order of element
+insertion (which is why they don't support s[i]).
+
+The following classes are provided:
+
+BaseSet -- All the operations common to both mutable and immutable
+ sets. This is an abstract class, not meant to be directly
+ instantiated.
+
+Set -- Mutable sets, subclass of BaseSet; not hashable.
+
+ImmutableSet -- Immutable sets, subclass of BaseSet; hashable.
+ An iterable argument is mandatory to create an ImmutableSet.
+
+_TemporarilyImmutableSet -- A wrapper around a Set, hashable,
+ giving the same hash value as the immutable set equivalent
+ would have. Do not use this class directly.
+
+Only hashable objects can be added to a Set. In particular, you cannot
+really add a Set as an element to another Set; if you try, what is
+actually added is an ImmutableSet built from it (it compares equal to
+the one you tried adding).
+
+When you ask if `x in y' where x is a Set and y is a Set or
+ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and
+what's tested is actually `z in y'.
+
+"""
+
+# Code history:
+#
+# - Greg V. Wilson wrote the first version, using a different approach
+# to the mutable/immutable problem, and inheriting from dict.
+#
+# - Alex Martelli modified Greg's version to implement the current
+# Set/ImmutableSet approach, and make the data an attribute.
+#
+# - Guido van Rossum rewrote much of the code, made some API changes,
+# and cleaned up the docstrings.
+#
+# - Raymond Hettinger added a number of speedups and other
+# improvements.
+
+from __future__ import generators
+try:
+ from itertools import ifilter, ifilterfalse
+except ImportError:
+ # Code to make the module run under Py2.2
+ def ifilter(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if predicate(x):
+ yield x
+ def ifilterfalse(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if not predicate(x):
+ yield x
+ try:
+ True, False
+ except NameError:
+ True, False = (0==0, 0!=0)
+
+__all__ = ['BaseSet', 'Set', 'ImmutableSet']
+
+class BaseSet(object):
+ """Common base class for mutable and immutable sets."""
+
+ __slots__ = ['_data']
+
+ # Constructor
+
+ def __init__(self):
+ """This is an abstract class."""
+ # Don't call this from a concrete subclass!
+ if self.__class__ is BaseSet:
+ raise TypeError, ("BaseSet is an abstract class. "
+ "Use Set or ImmutableSet.")
+
+ # Standard protocols: __len__, __repr__, __str__, __iter__
+
+ def __len__(self):
+ """Return the number of elements of a set."""
+ return len(self._data)
+
+ def __repr__(self):
+ """Return string representation of a set.
+
+ This looks like 'Set([<list of elements>])'.
+ """
+ return self._repr()
+
+ # __str__ is the same as __repr__
+ __str__ = __repr__
+
+ def _repr(self, sorted=False):
+ elements = self._data.keys()
+ if sorted:
+ elements.sort()
+ return '%s(%r)' % (self.__class__.__name__, elements)
+
+ def __iter__(self):
+ """Return an iterator over the elements or a set.
+
+ This is the keys iterator for the underlying dict.
+ """
+ return self._data.iterkeys()
+
+ # Three-way comparison is not supported. However, because __eq__ is
+ # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and
+ # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this
+ # case).
+
+ def __cmp__(self, other):
+ raise TypeError, "can't compare sets using cmp()"
+
+ # Equality comparisons using the underlying dicts. Mixed-type comparisons
+ # are allowed here, where Set == z for non-Set z always returns False,
+ # and Set != z always True. This allows expressions like "x in y" to
+ # give the expected result when y is a sequence of mixed types, not
+ # raising a pointless TypeError just because y contains a Set, or x is
+ # a Set and y contain's a non-set ("in" invokes only __eq__).
+ # Subtle: it would be nicer if __eq__ and __ne__ could return
+ # NotImplemented instead of True or False. Then the other comparand
+ # would get a chance to determine the result, and if the other comparand
+ # also returned NotImplemented then it would fall back to object address
+ # comparison (which would always return False for __eq__ and always
+ # True for __ne__). However, that doesn't work, because this type
+ # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented,
+ # Python tries __cmp__ next, and the __cmp__ here then raises TypeError.
+
+ def __eq__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data == other._data
+ else:
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data != other._data
+ else:
+ return True
+
+ # Copying operations
+
+ def copy(self):
+ """Return a shallow copy of a set."""
+ result = self.__class__()
+ result._data.update(self._data)
+ return result
+
+ __copy__ = copy # For the copy module
+
+ def __deepcopy__(self, memo):
+ """Return a deep copy of a set; used by copy module."""
+ # This pre-creates the result and inserts it in the memo
+ # early, in case the deep copy recurses into another reference
+ # to this same set. A set can't be an element of itself, but
+ # it can certainly contain an object that has a reference to
+ # itself.
+ from copy import deepcopy
+ result = self.__class__()
+ memo[id(self)] = result
+ data = result._data
+ value = True
+ for elt in self:
+ data[deepcopy(elt, memo)] = value
+ return result
+
+ # Standard set operations: union, intersection, both differences.
+ # Each has an operator version (e.g. __or__, invoked with |) and a
+ # method version (e.g. union).
+ # Subtle: Each pair requires distinct code so that the outcome is
+ # correct when the type of other isn't suitable. For example, if
+ # we did "union = __or__" instead, then Set().union(3) would return
+ # NotImplemented instead of raising TypeError (albeit that *why* it
+ # raises TypeError as-is is also a bit subtle).
+
+ def __or__(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.union(other)
+
+ def union(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ result = self.__class__(self)
+ result._update(other)
+ return result
+
+ def __and__(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.intersection(other)
+
+ def intersection(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if len(self) <= len(other):
+ little, big = self, other
+ else:
+ little, big = other, self
+ common = ifilter(big._data.has_key, little)
+ return self.__class__(common)
+
+ def __xor__(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.symmetric_difference(other)
+
+ def symmetric_difference(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ result = self.__class__()
+ data = result._data
+ value = True
+ selfdata = self._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ for elt in ifilterfalse(otherdata.has_key, selfdata):
+ data[elt] = value
+ for elt in ifilterfalse(selfdata.has_key, otherdata):
+ data[elt] = value
+ return result
+
+ def __sub__(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.difference(other)
+
+ def difference(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ result = self.__class__()
+ data = result._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ value = True
+ for elt in ifilterfalse(otherdata.has_key, self):
+ data[elt] = value
+ return result
+
+ # Membership test
+
+ def __contains__(self, element):
+ """Report whether an element is a member of a set.
+
+ (Called in response to the expression `element in self'.)
+ """
+ try:
+ return element in self._data
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ return transform() in self._data
+
+ # Subset and superset test
+
+ def issubset(self, other):
+ """Report whether another set contains this set."""
+ self._binary_sanity_check(other)
+ if len(self) > len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(other._data.has_key, self):
+ return False
+ return True
+
+ def issuperset(self, other):
+ """Report whether this set contains another set."""
+ self._binary_sanity_check(other)
+ if len(self) < len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(self._data.has_key, other):
+ return False
+ return True
+
+ # Inequality comparisons using the is-subset relation.
+ __le__ = issubset
+ __ge__ = issuperset
+
+ def __lt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) < len(other) and self.issubset(other)
+
+ def __gt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) > len(other) and self.issuperset(other)
+
+ # Assorted helpers
+
+ def _binary_sanity_check(self, other):
+ # Check that the other argument to a binary operation is also
+ # a set, raising a TypeError otherwise.
+ if not isinstance(other, BaseSet):
+ raise TypeError, "Binary operation only permitted between sets"
+
+ def _compute_hash(self):
+ # Calculate hash code for a set by xor'ing the hash codes of
+ # the elements. This ensures that the hash code does not depend
+ # on the order in which elements are added to the set. This is
+ # not called __hash__ because a BaseSet should not be hashable;
+ # only an ImmutableSet is hashable.
+ result = 0
+ for elt in self:
+ result ^= hash(elt)
+ return result
+
+ def _update(self, iterable):
+ # The main loop for update() and the subclass __init__() methods.
+ data = self._data
+
+ # Use the fast update() method when a dictionary is available.
+ if isinstance(iterable, BaseSet):
+ data.update(iterable._data)
+ return
+
+ value = True
+
+ if type(iterable) in (list, tuple, xrange):
+ # Optimized: we know that __iter__() and next() can't
+ # raise TypeError, so we can move 'try:' out of the loop.
+ it = iter(iterable)
+ while True:
+ try:
+ for element in it:
+ data[element] = value
+ return
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+ else:
+ # Safe: only catch TypeError where intended
+ for element in iterable:
+ try:
+ data[element] = value
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+
+
+class ImmutableSet(BaseSet):
+ """Immutable set class."""
+
+ __slots__ = ['_hashcode']
+
+ # BaseSet + hashing
+
+ def __init__(self, iterable=None):
+ """Construct an immutable set from an optional iterable."""
+ self._hashcode = None
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __hash__(self):
+ if self._hashcode is None:
+ self._hashcode = self._compute_hash()
+ return self._hashcode
+
+ def __getstate__(self):
+ return self._data, self._hashcode
+
+ def __setstate__(self, state):
+ self._data, self._hashcode = state
+
+class Set(BaseSet):
+ """ Mutable set class."""
+
+ __slots__ = []
+
+ # BaseSet + operations requiring mutability; no hashing
+
+ def __init__(self, iterable=None):
+ """Construct a set from an optional iterable."""
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __getstate__(self):
+ # getstate's results are ignored if it is not
+ return self._data,
+
+ def __setstate__(self, data):
+ self._data, = data
+
+ def __hash__(self):
+ """A Set cannot be hashed."""
+ # We inherit object.__hash__, so we must deny this explicitly
+ raise TypeError, "Can't hash a Set, only an ImmutableSet."
+
+ # In-place union, intersection, differences.
+ # Subtle: The xyz_update() functions deliberately return None,
+ # as do all mutating operations on built-in container types.
+ # The __xyz__ spellings have to return self, though.
+
+ def __ior__(self, other):
+ """Update a set with the union of itself and another."""
+ self._binary_sanity_check(other)
+ self._data.update(other._data)
+ return self
+
+ def union_update(self, other):
+ """Update a set with the union of itself and another."""
+ self._update(other)
+
+ def __iand__(self, other):
+ """Update a set with the intersection of itself and another."""
+ self._binary_sanity_check(other)
+ self._data = (self & other)._data
+ return self
+
+ def intersection_update(self, other):
+ """Update a set with the intersection of itself and another."""
+ if isinstance(other, BaseSet):
+ self &= other
+ else:
+ self._data = (self.intersection(other))._data
+
+ def __ixor__(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ self._binary_sanity_check(other)
+ self.symmetric_difference_update(other)
+ return self
+
+ def symmetric_difference_update(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ data = self._data
+ value = True
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in other:
+ if elt in data:
+ del data[elt]
+ else:
+ data[elt] = value
+
+ def __isub__(self, other):
+ """Remove all elements of another set from this set."""
+ self._binary_sanity_check(other)
+ self.difference_update(other)
+ return self
+
+ def difference_update(self, other):
+ """Remove all elements of another set from this set."""
+ data = self._data
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in ifilter(data.has_key, other):
+ del data[elt]
+
+ # Python dict-like mass mutations: update, clear
+
+ def update(self, iterable):
+ """Add all values from an iterable (such as a list or file)."""
+ self._update(iterable)
+
+ def clear(self):
+ """Remove all elements from this set."""
+ self._data.clear()
+
+ # Single-element mutations: add, remove, discard
+
+ def add(self, element):
+ """Add an element to a set.
+
+ This has no effect if the element is already present.
+ """
+ try:
+ self._data[element] = True
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ self._data[transform()] = True
+
+ def remove(self, element):
+ """Remove an element from a set; it must be a member.
+
+ If the element is not a member, raise a KeyError.
+ """
+ try:
+ del self._data[element]
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ del self._data[transform()]
+
+ def discard(self, element):
+ """Remove an element from a set if it is a member.
+
+ If the element is not a member, do nothing.
+ """
+ try:
+ self.remove(element)
+ except KeyError:
+ pass
+
+ def pop(self):
+ """Remove and return an arbitrary set element."""
+ return self._data.popitem()[0]
+
+ def __as_immutable__(self):
+ # Return a copy of self as an immutable set
+ return ImmutableSet(self)
+
+ def __as_temporarily_immutable__(self):
+ # Return self wrapped in a temporarily immutable set
+ return _TemporarilyImmutableSet(self)
+
+
+class _TemporarilyImmutableSet(BaseSet):
+ # Wrap a mutable set as if it was temporarily immutable.
+ # This only supplies hashing and equality comparisons.
+
+ def __init__(self, set):
+ self._set = set
+ self._data = set._data # Needed by ImmutableSet.__eq__()
+
+ def __hash__(self):
+ return self._set._compute_hash()
diff --git a/src/engine/SCons/compat/_sets15.py b/src/engine/SCons/compat/_sets15.py
new file mode 100644
index 0000000..b3d0bb3
--- /dev/null
+++ b/src/engine/SCons/compat/_sets15.py
@@ -0,0 +1,159 @@
+#
+# A Set class that works all the way back to Python 1.5. From:
+#
+# Python Cookbook: Yet another Set class for Python
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469
+# Goncalo Rodriques
+#
+# This is a pure Pythonic implementation of a set class. The syntax
+# and methods implemented are, for the most part, borrowed from
+# PEP 218 by Greg Wilson.
+#
+# Note that this class violates the formal definition of a set() by adding
+# a __getitem__() method so we can iterate over a set's elements under
+# Python 1.5 and 2.1, which don't support __iter__() and iterator types.
+#
+
+import string
+
+class Set:
+ """The set class. It can contain mutable objects."""
+
+ def __init__(self, seq = None):
+ """The constructor. It can take any object giving an iterator as an optional
+ argument to populate the new set."""
+ self.elems = []
+ if seq:
+ for elem in seq:
+ if elem not in self.elems:
+ self.elems.append(elem)
+
+ def __str__(self):
+ return "{%s}" % string.join(map(str, self.elems), ", ")
+
+
+ def copy(self):
+ """Shallow copy of a set object."""
+ return Set(self.elems)
+
+ def __contains__(self, elem):
+ return elem in self.elems
+
+ def __len__(self):
+ return len(self.elems)
+
+ def __getitem__(self, index):
+ # Added so that Python 1.5 can iterate over the elements.
+ # The cookbook recipe's author didn't like this because there
+ # really isn't any order in a set object, but this is necessary
+ # to make the class work well enough for our purposes.
+ return self.elems[index]
+
+ def items(self):
+ """Returns a list of the elements in the set."""
+ return self.elems
+
+ def add(self, elem):
+ """Add one element to the set."""
+ if elem not in self.elems:
+ self.elems.append(elem)
+
+ def remove(self, elem):
+ """Remove an element from the set. Return an error if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ raise LookupError, "Object %s is not a member of the set." % str(elem)
+
+ def discard(self, elem):
+ """Remove an element from the set. Do nothing if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ pass
+
+ def sort(self, func=cmp):
+ self.elems.sort(func)
+
+ #Define an iterator for a set.
+ def __iter__(self):
+ return iter(self.elems)
+
+ #The basic binary operations with sets.
+ def __or__(self, other):
+ """Union of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ if elem not in ret:
+ ret.elems.append(elem)
+ return ret
+
+ def __sub__(self, other):
+ """Difference of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ ret.discard(elem)
+ return ret
+
+ def __and__(self, other):
+ """Intersection of two sets."""
+ ret = Set()
+ for elem in self.elems:
+ if elem in other.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __add__(self, other):
+ """Symmetric difference of two sets."""
+ ret = Set()
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.elems.remove(elem)
+ else:
+ ret.elems.append(elem)
+ #Add remaining elements.
+ for elem in temp.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __mul__(self, other):
+ """Cartesian product of two sets."""
+ ret = Set()
+ for elemself in self.elems:
+ x = map(lambda other, s=elemself: (s, other), other.elems)
+ ret.elems.extend(x)
+ return ret
+
+ #Some of the binary comparisons.
+ def __lt__(self, other):
+ """Returns 1 if the lhs set is contained but not equal to the rhs set."""
+ if len(self.elems) < len(other.elems):
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.remove(elem)
+ else:
+ return 0
+ return len(temp.elems) == 0
+ else:
+ return 0
+
+ def __le__(self, other):
+ """Returns 1 if the lhs set is contained in the rhs set."""
+ if len(self.elems) <= len(other.elems):
+ ret = 1
+ for elem in self.elems:
+ if elem not in other.elems:
+ ret = 0
+ break
+ return ret
+ else:
+ return 0
+
+ def __eq__(self, other):
+ """Returns 1 if the sets are equal."""
+ if len(self.elems) != len(other.elems):
+ return 0
+ else:
+ return len(self - other) == 0
diff --git a/src/engine/SCons/compat/_subprocess.py b/src/engine/SCons/compat/_subprocess.py
index aa17cae..fc06347 100644
--- a/src/engine/SCons/compat/_subprocess.py
+++ b/src/engine/SCons/compat/_subprocess.py
@@ -356,6 +356,7 @@ import sys
mswindows = (sys.platform == "win32")
import os
+import string
import types
import traceback
@@ -565,7 +566,7 @@ def list2cmdline(seq):
result.extend(bs_buf)
result.append('"')
- return ''.join(result)
+ return string.join(result, '')
try:
@@ -1048,12 +1049,8 @@ class Popen(object):
# Close pipe fds. Make sure we don't close the same
# fd more than once, or standard fds.
- if p2cread:
- os.close(p2cread)
- if c2pwrite and c2pwrite not in (p2cread,):
- os.close(c2pwrite)
- if errwrite and errwrite not in (p2cread, c2pwrite):
- os.close(errwrite)
+ for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)):
+ if fd: os.close(fd)
# Close all other fds, if asked for
if close_fds:
@@ -1079,7 +1076,7 @@ class Popen(object):
exc_lines = traceback.format_exception(exc_type,
exc_value,
tb)
- exc_value.child_traceback = ''.join(exc_lines)
+ exc_value.child_traceback = string.join(exc_lines, '')
os.write(errpipe_write, pickle.dumps(exc_value))
# This exitcode won't be reported to applications, so it
@@ -1158,6 +1155,7 @@ class Popen(object):
read_set.append(self.stderr)
stderr = []
+ input_offset = 0
while read_set or write_set:
rlist, wlist, xlist = select.select(read_set, write_set, [])
@@ -1165,9 +1163,9 @@ class Popen(object):
# When select has indicated that the file is writable,
# we can write up to PIPE_BUF bytes without risk
# blocking. POSIX defines PIPE_BUF >= 512
- bytes_written = os.write(self.stdin.fileno(), input[:512])
- input = input[bytes_written:]
- if not input:
+ bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
+ input_offset = input_offset + bytes_written
+ if input_offset >= len(input):
self.stdin.close()
write_set.remove(self.stdin)
@@ -1187,9 +1185,9 @@ class Popen(object):
# All data exchanged. Translate lists into strings.
if stdout is not None:
- stdout = ''.join(stdout)
+ stdout = string.join(stdout, '')
if stderr is not None:
- stderr = ''.join(stderr)
+ stderr = string.join(stderr, '')
# Translate newlines, if requested. We cannot let the file
# object do the translation: It is based on stdio, which is
diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py
index d96ef23..8620936 100644
--- a/src/engine/SCons/cpp.py
+++ b/src/engine/SCons/cpp.py
@@ -32,7 +32,6 @@ import SCons.compat
import os
import re
import string
-import sys
#
# First "subsystem" of regular expressions that we set up:
diff --git a/src/script/scons-post-install.py b/src/script/scons-post-install.py
new file mode 100644
index 0000000..a622a6a
--- /dev/null
+++ b/src/script/scons-post-install.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# scons-post-install - SCons post install script for Windows
+#
+# A script for configuring "App Paths" registry key so that SCons could
+# be run from any directory the same way Python is.
+#
+
+#
+# SCons - a Software Constructor
+#
+# __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.path
+import sys
+
+scons_bat_path = os.path.join(sys.prefix, 'Scripts', 'scons.bat')
+
+app_paths_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SCons.bat'
+
+def install():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ print 'Writing "App Paths" registry entry for %s' % scons_bat_path
+ _winreg.SetValue(
+ _winreg.HKEY_LOCAL_MACHINE,
+ app_paths_key,
+ _winreg.REG_SZ,
+ scons_bat_path)
+ print 'Done.'
+
+
+def remove():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ # print 'Remove "App Paths" registry entry'
+ _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, app_paths_key)
+
+
+if len(sys.argv) > 1:
+ if sys.argv[1] == '-install':
+ install()
+ elif sys.argv[1] == '-remove':
+ remove()
+
+sys.exit(0)
diff --git a/src/script/scons.bat b/src/script/scons.bat
index 9e45a91..c0a304d 100644
--- a/src/script/scons.bat
+++ b/src/script/scons.bat
@@ -7,6 +7,7 @@ python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, '
@REM no way to set exit status of this script for 9x/Me
goto endscons
:WinNT
+set path=%path%;%~dp0
python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endscons
if errorlevel 9009 echo you do not have python in your PATH
diff --git a/src/setup.cfg b/src/setup.cfg
index f04ca1b..4b097ac 100644
--- a/src/setup.cfg
+++ b/src/setup.cfg
@@ -3,3 +3,4 @@ group = Development/Tools
[bdist_wininst]
title = SCons - a software construction tool
+install-script = scons-post-install.py
diff --git a/src/setup.py b/src/setup.py
index 9ad0069..53b19fa 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -350,9 +350,35 @@ class install_data(_install_data):
else:
self.data_files = []
+description = """Open Source next-generation build tool.
+Improved, cross-platform substitute for the classic Make
+utility. In short, SCons is an easier, more reliable
+and faster way to build software."""
+
+scripts = [
+ 'script/scons',
+ 'script/sconsign',
+ 'script/scons-time',
+
+ # We include scons.bat in the list of scripts, even on UNIX systems,
+ # because we provide an option to allow it be installed explicitly,
+ # for example if you're installing from UNIX on a share that's
+ # accessible to Windows and you want the scons.bat.
+ 'script/scons.bat',
+]
+
+if is_win32:
+ scripts = scripts + [
+ 'script/scons-post-install.py'
+ ]
+
arguments = {
'name' : "scons",
'version' : Version,
+ 'description' : description,
+ 'author' : 'Steven Knight',
+ 'author_email' : 'knight@baldmt.com',
+ 'url' : "http://www.scons.org/",
'packages' : ["SCons",
"SCons.compat",
"SCons.Node",
@@ -365,10 +391,7 @@ arguments = {
"SCons.Tool"],
'package_dir' : {'' : 'engine'},
'data_files' : [('man/man1', man_pages)],
- 'scripts' : ['script/scons',
- 'script/sconsign',
- 'script/scons-time',
- 'script/scons.bat'],
+ 'scripts' : scripts,
'cmdclass' : {'install' : install,
'install_lib' : install_lib,
'install_data' : install_data,
diff --git a/src/test_pychecker.py b/src/test_pychecker.py
new file mode 100644
index 0000000..368520f
--- /dev/null
+++ b/src/test_pychecker.py
@@ -0,0 +1,145 @@
+#!/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__"
+
+"""
+Use pychecker to catch various Python coding errors.
+"""
+
+import os
+import os.path
+import string
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_test('Not ready for clean pychecker output; skipping test.\n')
+
+try:
+ import pychecker
+except ImportError:
+ pychecker = test.where_is('pychecker')
+ if not pychecker:
+ test.skip_test("Could not find 'pychecker'; skipping test(s).\n")
+ program = pychecker
+ default_arguments = []
+else:
+ pychecker = os.path.join(os.path.split(pychecker.__file__)[0], 'checker.py')
+ program = sys.executable
+ default_arguments = [pychecker]
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ src_engine = os.environ['SCONS_LIB_DIR']
+else:
+ src_engine = os.path.join(cwd, 'build', 'scons-src', 'src', 'engine')
+ if not os.path.exists(src_engine):
+ src_engine = os.path.join(cwd, 'src', 'engine')
+
+src_engine_ = os.path.join(src_engine, '')
+
+MANIFEST = os.path.join(src_engine, 'MANIFEST.in')
+files = string.split(open(MANIFEST).read())
+
+files = filter(lambda f: f[-3:] == '.py', files)
+
+ignore = [
+ 'SCons/compat/__init__.py',
+ 'SCons/compat/builtins.py',
+ 'SCons/compat/_subprocess.py',
+ 'SCons/Optik/__init__.py',
+ 'SCons/Optik/errors.py',
+ 'SCons/Optik/option.py',
+ 'SCons/Optik/option_parser.py',
+]
+
+u = {}
+for file in files:
+ u[file] = 1
+for file in ignore:
+ try:
+ del u[file]
+ except KeyError:
+ pass
+
+files = u.keys()
+
+files.sort()
+
+mismatches = []
+
+default_arguments.extend([
+ '--quiet',
+ '--limit=1000',
+])
+
+if sys.platform == 'win32':
+ default_arguments.extend([
+ '--blacklist', '"pywintypes,pywintypes.error"',
+ ])
+
+per_file_arguments = {
+ 'SCons/__init__.py' : [
+ '--varlist', '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"',
+ ],
+}
+
+pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n"
+
+os.environ['PYTHONPATH'] = src_engine
+
+for file in files:
+
+ file = os.path.join(src_engine, file)
+ args = default_arguments + per_file_arguments.get(file, []) + [file]
+
+ test.run(program=program, arguments=args, status=None, stderr=None)
+
+ stdout = test.stdout()
+ stdout = string.replace(stdout, src_engine_, '')
+
+ stderr = test.stderr()
+ stderr = string.replace(stderr, src_engine_, '')
+ stderr = string.replace(stderr, pywintypes_warning, '')
+
+ if test.status or stdout or stderr:
+ mismatches.append('\n')
+ mismatches.append(string.join([program] + args) + '\n')
+
+ mismatches.append('STDOUT =====================================\n')
+ mismatches.append(stdout)
+
+ if stderr:
+ mismatches.append('STDERR =====================================\n')
+ mismatches.append(stderr)
+
+if mismatches:
+ print string.join(mismatches[1:], '')
+ test.fail_test()
+
+test.pass_test()
diff --git a/src/test_strings.py b/src/test_strings.py
index 9220231..b609f59 100644
--- a/src/test_strings.py
+++ b/src/test_strings.py
@@ -114,6 +114,8 @@ check_list = [
'src',
search_list = [ '*.py' ],
remove_list = [
+ 'engine/SCons/compat/_sets.py',
+ 'engine/SCons/compat/_sets15.py',
'engine/SCons/compat/_subprocess.py',
'engine/SCons/Conftest.py',
'engine/SCons/dblite.py',
@@ -134,6 +136,9 @@ check_list = [
'configure-stamp',
'debian',
'dist',
+ 'gentoo',
+ 'engine/SCons/compat/_sets.py',
+ 'engine/SCons/compat/_sets15.py',
'engine/SCons/compat/_subprocess.py',
'engine/SCons/Conftest.py',
'engine/SCons/dblite.py',
@@ -147,6 +152,8 @@ check_list = [
CheckExpandedCopyright(
build_local,
remove_list = [
+ 'SCons/compat/_sets.py',
+ 'SCons/compat/_sets15.py',
'SCons/compat/_subprocess.py',
'SCons/Conftest.py',
'SCons/dblite.py',
@@ -160,10 +167,12 @@ check_list = [
'bin',
'config',
'debian',
+ 'gentoo',
'doc/design',
'doc/MANIFEST',
'doc/python10',
'doc/reference',
+ 'doc/developer/MANIFEST',
'doc/man/MANIFEST',
'doc/user/cons.pl',
'doc/user/MANIFEST',
@@ -183,6 +192,8 @@ check_list = [
'src/engine/MANIFEST.in',
'src/engine/MANIFEST-xml.in',
'src/engine/setup.cfg',
+ 'src/engine/SCons/compat/_sets.py',
+ 'src/engine/SCons/compat/_sets15.py',
'src/engine/SCons/compat/_subprocess.py',
'src/engine/SCons/Conftest.py',
'src/engine/SCons/dblite.py',