import os
import os.path
+import errno
import shutil
import stat
-import string
import time
-import types
+import sys
import SCons.Action
import SCons.Builder
+import SCons.CacheDir
import SCons.Environment
-import SCons.Scanner.C
-import SCons.Scanner.D
-import SCons.Scanner.Prog
-import SCons.Sig
+import SCons.PathList
+import SCons.Subst
+import SCons.Tool
# A placeholder for a default Environment (for fetching source files
# from source code management systems and the like). This must be
# Lazily instantiate the default environment so the overhead of creating
# it doesn't apply when it's not needed.
+def _fetch_DefaultEnvironment(*args, **kw):
+ """
+ Returns the already-created default construction environment.
+ """
+ global _default_env
+ return _default_env
+
def DefaultEnvironment(*args, **kw):
+ """
+ Initial public entry point for creating the default construction
+ Environment.
+
+ After creating the environment, we overwrite our name
+ (DefaultEnvironment) with the _fetch_DefaultEnvironment() function,
+ which more efficiently returns the initialized default construction
+ environment without checking for its existence.
+
+ (This function still exists with its _default_check because someone
+ else (*cough* Script/__init__.py *cough*) may keep a reference
+ to this function. So we can't use the fully functional idiom of
+ having the name originally be a something that *only* creates the
+ construction environment and then overwrites the name.)
+ """
global _default_env
if not _default_env:
- _default_env = apply(SCons.Environment.Environment, args, kw)
- _default_env._build_signature = 1
- _default_env._calc_module = SCons.Sig.default_module
+ import SCons.Util
+ _default_env = SCons.Environment.Environment(*args, **kw)
+ if SCons.Util.md5:
+ _default_env.Decider('MD5')
+ else:
+ _default_env.Decider('timestamp-match')
+ global DefaultEnvironment
+ DefaultEnvironment = _fetch_DefaultEnvironment
+ _default_env._CacheDir_path = None
return _default_env
# Emitters for setting the shared attribute on object files,
SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
-# Scanners and suffixes for common languages.
-ObjSourceScan = SCons.Scanner.Scanner({}, name='ObjSrcScanner')
-
-CScan = SCons.Scanner.C.CScan()
-
-CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
- ".h", ".H", ".hxx", ".hpp", ".hh",
- ".F", ".fpp", ".FPP",
- ".S", ".spp", ".SPP"]
-
-for suffix in CSuffixes:
- ObjSourceScan.add_scanner(suffix, CScan)
-
-DScan = SCons.Scanner.D.DScan()
-
-DSuffixes = ['.d']
-
-for suffix in DSuffixes:
- ObjSourceScan.add_scanner(suffix, DScan)
-
-IDLSuffixes = [".idl", ".IDL"]
-
-# cleanup
-del suffix
+# Some people were using these variable name before we made
+# SourceFileScanner part of the public interface. Don't break their
+# SConscript files until we've given them some fair warning and a
+# transition period.
+CScan = SCons.Tool.CScanner
+DScan = SCons.Tool.DScanner
+LaTeXScan = SCons.Tool.LaTeXScanner
+ObjSourceScan = SCons.Tool.SourceFileScanner
+ProgScan = SCons.Tool.ProgramScanner
+
+# These aren't really tool scanners, so they don't quite belong with
+# the rest of those in Tool/__init__.py, but I'm not sure where else
+# they should go. Leave them here for now.
+import SCons.Scanner.Dir
+DirScanner = SCons.Scanner.Dir.DirScanner()
+DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
# Actions for common languages.
CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
-DAction = SCons.Action.Action("$DCOM")
-ShCAction = SCons.Action.Action("$SHCCCOM")
+ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
-ShCXXAction = SCons.Action.Action("$SHCXXCOM")
+ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
-ASPPAction = SCons.Action.Action("$ASPPCOM")
+ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
-LinkAction = SCons.Action.Action("$LINKCOM")
-ShLinkAction = SCons.Action.Action("$SHLINKCOM")
+LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
+ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
-ArAction = SCons.Action.Action("$ARCOM")
-
-LexAction = SCons.Action.Action("$LEXCOM")
-YaccAction = SCons.Action.Action("$YACCCOM")
-
-ProgScan = SCons.Scanner.Prog.ProgScan()
-
-def DVI():
- """Common function to generate a DVI file Builder."""
- return SCons.Builder.Builder(action = {},
- # The suffix is not configurable via a
- # construction variable like $DVISUFFIX
- # because the output file name is
- # hard-coded within TeX.
- suffix = '.dvi')
-
-def PDF():
- """A function for generating the PDF Builder."""
- return SCons.Builder.Builder(action = { },
- prefix = '$PDFPREFIX',
- suffix = '$PDFSUFFIX')
+LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
# Common tasks that we allow users to perform in platform-independent
# ways by creating ActionFactory instances.
ActionFactory = SCons.Action.ActionFactory
-Chmod = ActionFactory(os.chmod,
- lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
+def get_paths_str(dest):
+ # If dest is a list, we need to manually call str() on each element
+ if SCons.Util.is_List(dest):
+ elem_strs = []
+ for element in dest:
+ elem_strs.append('"' + str(element) + '"')
+ return '[' + ', '.join(elem_strs) + ']'
+ else:
+ return '"' + str(dest) + '"'
+
+def chmod_func(dest, mode):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for element in dest:
+ os.chmod(str(element), mode)
-def Copy(dest, src):
- def _copy_func(target, source, env, dest=dest, src=src):
- dest = str(env.arg2nodes(dest, env.fs.Entry)[0])
- src = str(env.arg2nodes(src, env.fs.Entry)[0])
- shutil.copytree(src, dest, 1)
- def _copy_str(target, source, env, dest=dest, src=src):
- dest = str(env.arg2nodes(dest, env.fs.Entry)[0])
- src = str(env.arg2nodes(src, env.fs.Entry)[0])
- return 'Copy("%s", "%s")' % (dest, src)
- return SCons.Action.Action(_copy_func, strfunction=_copy_str)
+def chmod_strfunc(dest, mode):
+ return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
+
+Chmod = ActionFactory(chmod_func, chmod_strfunc)
def copy_func(dest, src):
- if os.path.isfile(src):
- return shutil.copy(src, dest)
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if SCons.Util.is_List(src) and os.path.isdir(dest):
+ for file in src:
+ shutil.copy2(file, dest)
+ return 0
+ elif os.path.isfile(src):
+ return shutil.copy2(src, dest)
else:
return shutil.copytree(src, dest, 1)
Copy = ActionFactory(copy_func,
- lambda dest, src: 'Copy("%s", "%s")' % (dest, src))
-
-def delete_func(entry, must_exist=0):
- if not must_exist and not os.path.exists(entry):
- return None
- if not os.path.exists(entry) or os.path.isfile(entry):
- return os.unlink(entry)
- else:
- return shutil.rmtree(entry, 1)
+ lambda dest, src: 'Copy("%s", "%s")' % (dest, src),
+ convert=str)
+
+def delete_func(dest, must_exist=0):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for entry in dest:
+ entry = str(entry)
+ if not must_exist and not os.path.exists(entry):
+ continue
+ if not os.path.exists(entry) or os.path.isfile(entry):
+ os.unlink(entry)
+ continue
+ else:
+ shutil.rmtree(entry, 1)
+ continue
-def delete_strfunc(entry, must_exist=0):
- return 'Delete("%s")' % entry
+def delete_strfunc(dest, must_exist=0):
+ return 'Delete(%s)' % get_paths_str(dest)
Delete = ActionFactory(delete_func, delete_strfunc)
-Mkdir = ActionFactory(os.makedirs,
- lambda dir: 'Mkdir("%s")' % dir)
-
-Move = ActionFactory(lambda dest, src: os.rename(src, dest),
- lambda dest, src: 'Move("%s", "%s")' % (dest, src))
-
-def touch_func(file):
- mtime = int(time.time())
- if os.path.exists(file):
- atime = os.path.getatime(file)
- else:
- open(file, 'w')
- atime = mtime
- return os.utime(file, (atime, mtime))
+def mkdir_func(dest):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for entry in dest:
+ try:
+ os.makedirs(str(entry))
+ except os.error, e:
+ p = str(entry)
+ if (e[0] == errno.EEXIST or (sys.platform=='win32' and e[0]==183)) \
+ and os.path.isdir(str(entry)):
+ pass # not an error if already exists
+ else:
+ raise
+
+Mkdir = ActionFactory(mkdir_func,
+ lambda dir: 'Mkdir(%s)' % get_paths_str(dir))
+
+def move_func(dest, src):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ SCons.Node.FS.invalidate_node_memos(src)
+ shutil.move(src, dest)
+
+Move = ActionFactory(move_func,
+ lambda dest, src: 'Move("%s", "%s")' % (dest, src),
+ convert=str)
+
+def touch_func(dest):
+ SCons.Node.FS.invalidate_node_memos(dest)
+ if not SCons.Util.is_List(dest):
+ dest = [dest]
+ for file in dest:
+ file = str(file)
+ mtime = int(time.time())
+ if os.path.exists(file):
+ atime = os.path.getatime(file)
+ else:
+ open(file, 'w')
+ atime = mtime
+ os.utime(file, (atime, mtime))
Touch = ActionFactory(touch_func,
- lambda file: 'Touch("%s")' % file)
+ lambda file: 'Touch(%s)' % get_paths_str(file))
# Internal utility functions
-def copyFunc(dest, source, env):
- """Install a source file into a destination by copying it (and its
- permission/mode bits)."""
- shutil.copy2(source, dest)
- st = os.stat(source)
- os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
- return 0
-
-def _concat(prefix, list, suffix, env, f=lambda x: x):
- """Creates a new list from 'list' by first interpolating each
- element in the list using the 'env' dictionary and then calling f
- on the list, and finally concatenating 'prefix' and 'suffix' onto
- each element of the list. A trailing space on 'prefix' or leading
- space on 'suffix' will cause them to be put into separate list
- elements rather than being concatenated."""
-
+
+def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
+ """
+ Creates a new list from 'list' by first interpolating each element
+ in the list using the 'env' dictionary and then calling f on the
+ list, and finally calling _concat_ixes to concatenate 'prefix' and
+ 'suffix' onto each element of the list.
+ """
if not list:
return list
- if SCons.Util.is_List(list):
- list = SCons.Util.flatten(list)
- list = f(env.subst_path(list))
+ l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
+ if l is not None:
+ list = l
+
+ return _concat_ixes(prefix, list, suffix, env)
+
+def _concat_ixes(prefix, list, suffix, env):
+ """
+ Creates a new list from 'list' by concatenating the 'prefix' and
+ 'suffix' arguments onto each element of the list. A trailing space
+ on 'prefix' or leading space on 'suffix' will cause them to be put
+ into separate list elements rather than being concatenated.
+ """
result = []
# ensure that prefix and suffix are strings
- prefix = str(env.subst(prefix, SCons.Util.SUBST_RAW))
- suffix = str(env.subst(suffix, SCons.Util.SUBST_RAW))
+ prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
+ suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
for x in list:
if isinstance(x, SCons.Node.FS.File):
return result
-def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
- """This is a wrapper around _concat() that checks for the existence
- of prefixes or suffixes on list elements and strips them where it
- finds them. This is used by tools (like the GNU linker) that need
- to turn something like 'libfoo.a' into '-lfoo'."""
+def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
+ """
+ This is a wrapper around _concat()/_concat_ixes() that checks for
+ the existence of prefixes or suffixes on list items and strips them
+ where it finds them. This is used by tools (like the GNU linker)
+ that need to turn something like 'libfoo.a' into '-lfoo'.
+ """
+ if not itms:
+ return itms
+
if not callable(c):
- if callable(env["_concat"]):
- c = env["_concat"]
+ env_c = env['_concat']
+ if env_c != _concat and callable(env_c):
+ # There's a custom _concat() method in the construction
+ # environment, and we've allowed people to set that in
+ # the past (see test/custom-concat.py), so preserve the
+ # backwards compatibility.
+ c = env_c
else:
- c = _concat
- def f(list, sp=stripprefix, ss=stripsuffix):
- result = []
- for l in list:
- if isinstance(l, SCons.Node.FS.File):
- result.append(l)
- continue
- if not SCons.Util.is_String(l):
- l = str(l)
- if l[:len(sp)] == sp:
- l = l[len(sp):]
- if l[-len(ss):] == ss:
- l = l[:-len(ss)]
- result.append(l)
- return result
- return c(prefix, list, suffix, env, f)
-
-def _defines(prefix, defs, suffix, env, c=_concat):
- """A wrapper around _concat that turns a list or string
- into a list of C preprocessor command-line definitions.
+ c = _concat_ixes
+
+ stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes)))
+ stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes)))
+
+ stripped = []
+ for l in SCons.PathList.PathList(itms).subst_path(env, None, None):
+ if isinstance(l, SCons.Node.FS.File):
+ stripped.append(l)
+ continue
+
+ if not SCons.Util.is_String(l):
+ l = str(l)
+
+ for stripprefix in stripprefixes:
+ lsp = len(stripprefix)
+ if l[:lsp] == stripprefix:
+ l = l[lsp:]
+ # Do not strip more than one prefix
+ break
+
+ for stripsuffix in stripsuffixes:
+ lss = len(stripsuffix)
+ if l[-lss:] == stripsuffix:
+ l = l[:-lss]
+ # Do not strip more than one suffix
+ break
+
+ stripped.append(l)
+
+ return c(prefix, stripped, suffix, env)
+
+def processDefines(defs):
+ """process defines, resolving strings, lists, dictionaries, into a list of
+ strings
"""
if SCons.Util.is_List(defs):
l = []
for d in defs:
- if SCons.Util.is_List(d) or type(d) is types.TupleType:
+ if SCons.Util.is_List(d) or isinstance(d, tuple):
l.append(str(d[0]) + '=' + str(d[1]))
else:
l.append(str(d))
# Consequently, we have to sort the keys to ensure a
# consistent order...
l = []
- keys = defs.keys()
- keys.sort()
- for k in keys:
- v = defs[k]
+ for k,v in sorted(defs.items()):
if v is None:
l.append(str(k))
else:
l.append(str(k) + '=' + str(v))
else:
l = [str(defs)]
- return c(prefix, l, suffix, env)
+ return l
+
+def _defines(prefix, defs, suffix, env, c=_concat_ixes):
+ """A wrapper around _concat_ixes that turns a list or string
+ into a list of C preprocessor command-line definitions.
+ """
+
+ return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
class NullCmdGenerator:
"""This is a callable class that can be used in place of other
def __call__(self, target, source, env, for_signature=None):
return self.cmd
+class Variable_Method_Caller:
+ """A class for finding a construction variable on the stack and
+ calling one of its methods.
+
+ We use this to support "construction variables" in our string
+ eval()s that actually stand in for methods--specifically, use
+ of "RDirs" in call to _concat that should actually execute the
+ "TARGET.RDirs" method. (We used to support this by creating a little
+ "build dictionary" that mapped RDirs to the method, but this got in
+ the way of Memoizing construction environments, because we had to
+ create new environment objects to hold the variables.)
+ """
+ def __init__(self, variable, method):
+ self.variable = variable
+ self.method = method
+ def __call__(self, *args, **kw):
+ try: 1/0
+ except ZeroDivisionError:
+ # Don't start iterating with the current stack-frame to
+ # prevent creating reference cycles (f_back is safe).
+ frame = sys.exc_info()[2].tb_frame.f_back
+ variable = self.variable
+ while frame:
+ if variable in frame.f_locals:
+ v = frame.f_locals[variable]
+ if v:
+ method = getattr(v, self.method)
+ return method(*args, **kw)
+ frame = frame.f_back
+ return None
+
ConstructionEnvironment = {
- 'BUILDERS' : {},
- 'SCANNERS' : [],
- 'CPPSUFFIXES': CSuffixes,
- 'DSUFFIXES' : DSuffixes,
- 'IDLSUFFIXES': IDLSuffixes,
- 'PDFPREFIX' : '',
- 'PDFSUFFIX' : '.pdf',
- 'PSPREFIX' : '',
- 'PSSUFFIX' : '.ps',
- 'ENV' : {},
- 'INSTALL' : copyFunc,
- '_concat' : _concat,
- '_defines' : _defines,
- '_stripixes' : _stripixes,
- '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
- '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs)} $)',
- '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)',
- '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
- 'TEMPFILE' : NullCmdGenerator
- }
+ 'BUILDERS' : {},
+ 'SCANNERS' : [],
+ 'CONFIGUREDIR' : '#/.sconf_temp',
+ 'CONFIGURELOG' : '#/config.log',
+ 'CPPSUFFIXES' : SCons.Tool.CSuffixes,
+ 'DSUFFIXES' : SCons.Tool.DSuffixes,
+ 'ENV' : {},
+ 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes,
+# 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions
+ '_concat' : _concat,
+ '_defines' : _defines,
+ '_stripixes' : _stripixes,
+ '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
+ '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+ 'TEMPFILE' : NullCmdGenerator,
+ 'Dir' : Variable_Method_Caller('TARGET', 'Dir'),
+ 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'),
+ 'File' : Variable_Method_Caller('TARGET', 'File'),
+ 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'),
+}
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: