+ Scons updated to latest stable version 1.2

This commit is contained in:
Artem Pavlenko 2009-01-06 10:52:43 +00:00
parent 8977683c0d
commit 8687dd795d
191 changed files with 7866 additions and 3194 deletions

View file

@ -33,38 +33,38 @@ else:
# All of the following options may be modified at the command-line, for example: # All of the following options may be modified at the command-line, for example:
# `python scons/scons PREFIX=/opt` # `python scons/scons PREFIX=/opt`
opts = Options('config.py') opts = Variables('config.py')
opts.Add('CXX', 'The C++ compiler to use (defaults to g++).', 'g++') opts.Add('CXX', 'The C++ compiler to use (defaults to g++).', 'g++')
opts.Add('PREFIX', 'The install path "prefix"', '/usr/local') opts.Add('PREFIX', 'The install path "prefix"', '/usr/local')
opts.Add(PathOption('BOOST_INCLUDES', 'Search path for boost include files', '/usr/include')) opts.Add(PathVariable('BOOST_INCLUDES', 'Search path for boost include files', '/usr/include'))
opts.Add(PathOption('BOOST_LIBS', 'Search path for boost library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('BOOST_LIBS', 'Search path for boost library files', '/usr/' + LIBDIR_SCHEMA))
opts.Add('BOOST_TOOLKIT','Specify boost toolkit, e.g., gcc41.','',False) opts.Add('BOOST_TOOLKIT','Specify boost toolkit, e.g., gcc41.','',False)
opts.Add('BOOST_ABI', 'Specify boost ABI, e.g., d.','',False) opts.Add('BOOST_ABI', 'Specify boost ABI, e.g., d.','',False)
opts.Add('BOOST_VERSION','Specify boost version, e.g., 1_35.','',False) opts.Add('BOOST_VERSION','Specify boost version, e.g., 1_35.','',False)
opts.Add(('FREETYPE_CONFIG', 'The path to the freetype-config executable.', 'freetype-config')) opts.Add(('FREETYPE_CONFIG', 'The path to the freetype-config executable.', 'freetype-config'))
opts.Add(('XML2_CONFIG', 'The path to the xml2-config executable.', 'xml2-config')) opts.Add(('XML2_CONFIG', 'The path to the xml2-config executable.', 'xml2-config'))
opts.Add(PathOption('ICU_INCLUDES', 'Search path for ICU include files', '/usr/include')) opts.Add(PathVariable('ICU_INCLUDES', 'Search path for ICU include files', '/usr/include'))
opts.Add(PathOption('ICU_LIBS','Search path for ICU include files','/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('ICU_LIBS','Search path for ICU include files','/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include')) opts.Add(PathVariable('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include'))
opts.Add(PathOption('PNG_LIBS','Search path for libpng include files','/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('PNG_LIBS','Search path for libpng include files','/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('JPEG_INCLUDES', 'Search path for libjpeg include files', '/usr/include')) opts.Add(PathVariable('JPEG_INCLUDES', 'Search path for libjpeg include files', '/usr/include'))
opts.Add(PathOption('JPEG_LIBS', 'Search path for libjpeg library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('JPEG_LIBS', 'Search path for libjpeg library files', '/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('TIFF_INCLUDES', 'Search path for libtiff include files', '/usr/include')) opts.Add(PathVariable('TIFF_INCLUDES', 'Search path for libtiff include files', '/usr/include'))
opts.Add(PathOption('TIFF_LIBS', 'Search path for libtiff library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('TIFF_LIBS', 'Search path for libtiff library files', '/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('PGSQL_INCLUDES', 'Search path for PostgreSQL include files', '/usr/include')) opts.Add(PathVariable('PGSQL_INCLUDES', 'Search path for PostgreSQL include files', '/usr/include'))
opts.Add(PathOption('PGSQL_LIBS', 'Search path for PostgreSQL library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('PGSQL_LIBS', 'Search path for PostgreSQL library files', '/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('PROJ_INCLUDES', 'Search path for PROJ.4 include files', '/usr/local/include')) opts.Add(PathVariable('PROJ_INCLUDES', 'Search path for PROJ.4 include files', '/usr/local/include'))
opts.Add(PathOption('PROJ_LIBS', 'Search path for PROJ.4 library files', '/usr/local/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('PROJ_LIBS', 'Search path for PROJ.4 library files', '/usr/local/' + LIBDIR_SCHEMA))
opts.Add(PathOption('GDAL_INCLUDES', 'Search path for GDAL include files', '/usr/include')) opts.Add(PathVariable('GDAL_INCLUDES', 'Search path for GDAL include files', '/usr/include'))
opts.Add(PathOption('GDAL_LIBS', 'Search path for GDAL library files', '/usr/' + LIBDIR_SCHEMA)) opts.Add(PathVariable('GDAL_LIBS', 'Search path for GDAL library files', '/usr/' + LIBDIR_SCHEMA))
opts.Add(PathOption('PYTHON','Python executable', sys.executable)) opts.Add(PathVariable('PYTHON','Python executable', sys.executable))
opts.Add(ListOption('INPUT_PLUGINS','Input drivers to include','all',['postgis','shape','raster','gdal'])) opts.Add(ListVariable('INPUT_PLUGINS','Input drivers to include','all',['postgis','shape','raster','gdal']))
opts.Add(ListOption('BINDINGS','Language bindings to build','all',['python'])) opts.Add(ListVariable('BINDINGS','Language bindings to build','all',['python']))
opts.Add(BoolOption('DEBUG', 'Compile a debug version of mapnik', 'False')) opts.Add(BoolVariable('DEBUG', 'Compile a debug version of mapnik', 'False'))
opts.Add('DESTDIR', 'The root directory to install into. Useful mainly for binary package building', '/') opts.Add('DESTDIR', 'The root directory to install into. Useful mainly for binary package building', '/')
opts.Add(EnumOption('THREADING','Set threading support','multi', ['multi','single'])) opts.Add(EnumVariable('THREADING','Set threading support','multi', ['multi','single']))
opts.Add(EnumOption('XMLPARSER','Set xml parser ','tinyxml', ['tinyxml','spirit','libxml2'])) opts.Add(EnumVariable('XMLPARSER','Set xml parser ','tinyxml', ['tinyxml','spirit','libxml2']))
opts.Add(BoolOption('INTERNAL_LIBAGG', 'Use provided libagg', 'True')) opts.Add(BoolVariable('INTERNAL_LIBAGG', 'Use provided libagg', 'True'))
env = Environment(ENV=os.environ, options=opts) env = Environment(ENV=os.environ, options=opts)

View file

@ -3,7 +3,7 @@
This copyright and license do not apply to any other software This copyright and license do not apply to any other software
with which this software may have been included. with which this software may have been included.
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View file

@ -1,4 +1,4 @@
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
SCons - a software construction tool SCons - a software construction tool

View file

@ -1,71 +0,0 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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.
#
"""SCons.Errors
This file contains the exception classes used to handle internal
and user errors in SCons.
"""
__revision__ = "src/engine/SCons/Errors.py 2523 2007/12/12 09:37:41 knight"
class BuildError(Exception):
def __init__(self, node=None, errstr="Unknown error", status=0,
filename=None, executor=None, action=None, command=None,
*args):
self.node = node
self.errstr = errstr
self.status = status
self.filename = filename
self.executor = executor
self.action = action
self.command = command
apply(Exception.__init__, (self,) + args)
class InternalError(Exception):
pass
class UserError(Exception):
pass
class StopError(Exception):
pass
class EnvironmentError(Exception):
pass
class ExplicitExit(Exception):
def __init__(self, node=None, status=None, *args):
self.node = node
self.status = status
apply(Exception.__init__, (self,) + args)
class TaskmasterException(Exception):
def __init__(self, node=None, exc_info=(None, None, None), *args):
self.node = node
self.errstr = "Exception"
self.exc_info = exc_info
apply(Exception.__init__, (self,) + args)

View file

@ -1,126 +0,0 @@
"""SCons.Scanner.LaTeX
This module implements the dependency scanner for LaTeX code.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Scanner/LaTeX.py 2523 2007/12/12 09:37:41 knight"
import os.path
import string
import SCons.Scanner
def LaTeXScanner():
"""Return a prototype Scanner instance for scanning LaTeX source files"""
ds = LaTeX(name = "LaTeXScanner",
suffixes = '$LATEXSUFFIXES',
path_variable = 'TEXINPUTS',
regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}',
recursive = 0)
return ds
class LaTeX(SCons.Scanner.Classic):
"""Class for scanning LaTeX files for included files.
Unlike most scanners, which use regular expressions that just
return the included file name, this returns a tuple consisting
of the keyword for the inclusion ("include", "includegraphics",
"input", or "bibliography"), and then the file name itself.
Based on a quick look at LaTeX documentation, it seems that we
need a should append .tex suffix for the "include" keywords,
append .tex if there is no extension for the "input" keyword,
but leave the file name untouched for "includegraphics." For
the "bibliography" keyword we need to add .bib if there is
no extension. (This need to be revisited since if there
is no extension for an :includegraphics" keyword latex will
append .ps or .eps to find the file; while pdftex will use
other extensions.)
"""
def latex_name(self, include):
filename = include[1]
if include[0] == 'input':
base, ext = os.path.splitext( filename )
if ext == "":
filename = filename + '.tex'
if (include[0] == 'include'):
filename = filename + '.tex'
if include[0] == 'bibliography':
base, ext = os.path.splitext( filename )
if ext == "":
filename = filename + '.bib'
if include[0] == 'usepackage':
base, ext = os.path.splitext( filename )
if ext == "":
filename = filename + '.sty'
return filename
def sort_key(self, include):
return SCons.Node.FS._my_normcase(self.latex_name(include))
def find_include(self, include, source_dir, path):
i = SCons.Node.FS.find_file(self.latex_name(include),
(source_dir,) + path)
return i, include
def scan(self, node, path=()):
#
# Modify the default scan function to allow for the regular
# expression to return a comma separated list of file names
# as can be the case with the bibliography keyword.
#
# cache the includes list in node so we only scan it once:
if node.includes != None:
includes = node.includes
else:
includes = self.cre.findall(node.get_contents())
node.includes = includes
# This is a hand-coded DSU (decorate-sort-undecorate, or
# Schwartzian transform) pattern. The sort key is the raw name
# of the file as specifed on the #include line (including the
# " or <, since that may affect what file is found), which lets
# us keep the sort order constant regardless of whether the file
# is actually found in a Repository or locally.
nodes = []
source_dir = node.get_dir()
for include in includes:
#
# Handle multiple filenames in include[1]
#
inc_list = string.split(include[1],',')
for j in range(len(inc_list)):
include_local = [include[0],inc_list[j]]
n, i = self.find_include(include_local, source_dir, path)
if n is None:
SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
"No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
else:
sortkey = self.sort_key(include)
nodes.append((sortkey, n))
nodes.sort()
nodes = map(lambda pair: pair[1], nodes)
return nodes

View file

@ -1,135 +0,0 @@
"""engine.SCons.Tool.f77
Tool-specific initialization for the generic Posix f77 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/f77.py 2523 2007/12/12 09:37:41 knight"
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
import fortran
compilers = ['f77']
#
F77Suffixes = ['.f77']
F77PPSuffixes = []
if SCons.Util.case_sensitive_suffixes('.f77', '.F77'):
F77PPSuffixes.append('.F77')
else:
F77Suffixes.append('.F77')
#
F77Scan = SCons.Scanner.Fortran.FortranScan("F77PATH")
for suffix in F77Suffixes + F77PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F77Scan)
del suffix
#
fVLG = fortran.VariableListGenerator
F77Generator = fVLG('F77', 'FORTRAN', '_FORTRAND')
F77FlagsGenerator = fVLG('F77FLAGS', 'FORTRANFLAGS')
F77CommandGenerator = fVLG('F77COM', 'FORTRANCOM', '_F77COMD')
F77CommandStrGenerator = fVLG('F77COMSTR', 'FORTRANCOMSTR', '_F77COMSTRD')
F77PPCommandGenerator = fVLG('F77PPCOM', 'FORTRANPPCOM', '_F77PPCOMD')
F77PPCommandStrGenerator = fVLG('F77PPCOMSTR', 'FORTRANPPCOMSTR', '_F77PPCOMSTRD')
ShF77Generator = fVLG('SHF77', 'SHFORTRAN', 'F77', 'FORTRAN', '_FORTRAND')
ShF77FlagsGenerator = fVLG('SHF77FLAGS', 'SHFORTRANFLAGS')
ShF77CommandGenerator = fVLG('SHF77COM', 'SHFORTRANCOM', '_SHF77COMD')
ShF77CommandStrGenerator = fVLG('SHF77COMSTR', 'SHFORTRANCOMSTR', '_SHF77COMSTRD')
ShF77PPCommandGenerator = fVLG('SHF77PPCOM', 'SHFORTRANPPCOM', '_SHF77PPCOMD')
ShF77PPCommandStrGenerator = fVLG('SHF77PPCOMSTR', 'SHFORTRANPPCOMSTR', '_SHF77PPCOMSTRD')
del fVLG
#
F77Action = SCons.Action.Action('$_F77COMG ', '$_F77COMSTRG')
F77PPAction = SCons.Action.Action('$_F77PPCOMG ', '$_F77PPCOMSTRG')
ShF77Action = SCons.Action.Action('$_SHF77COMG ', '$_SHF77COMSTRG')
ShF77PPAction = SCons.Action.Action('$_SHF77PPCOMG ', '$_SHF77PPCOMSTRG')
def add_to_env(env):
"""Add Builders and construction variables for f77 to an Environment."""
env.AppendUnique(FORTRANSUFFIXES = F77Suffixes + F77PPSuffixes)
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in F77Suffixes:
static_obj.add_action(suffix, F77Action)
shared_obj.add_action(suffix, ShF77Action)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
for suffix in F77PPSuffixes:
static_obj.add_action(suffix, F77PPAction)
shared_obj.add_action(suffix, ShF77PPAction)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
env['_F77G'] = F77Generator
env['_F77FLAGSG'] = F77FlagsGenerator
env['_F77COMG'] = F77CommandGenerator
env['_F77PPCOMG'] = F77PPCommandGenerator
env['_F77COMSTRG'] = F77CommandStrGenerator
env['_F77PPCOMSTRG'] = F77PPCommandStrGenerator
env['_SHF77G'] = ShF77Generator
env['_SHF77FLAGSG'] = ShF77FlagsGenerator
env['_SHF77COMG'] = ShF77CommandGenerator
env['_SHF77PPCOMG'] = ShF77PPCommandGenerator
env['_SHF77COMSTRG'] = ShF77CommandStrGenerator
env['_SHF77PPCOMSTRG'] = ShF77PPCommandStrGenerator
env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_F77COMD'] = '$_F77G -o $TARGET -c $_F77FLAGSG $_F77INCFLAGS $SOURCES'
env['_F77PPCOMD'] = '$_F77G -o $TARGET -c $_F77FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS $SOURCES'
env['_SHF77COMD'] = '$_SHF77G -o $TARGET -c $_SHF77FLAGSG $_F77INCFLAGS $SOURCES'
env['_SHF77PPCOMD'] = '$_SHF77G -o $TARGET -c $_SHF77FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS $SOURCES'
def generate(env):
fortran.add_to_env(env)
import f90
import f95
f90.add_to_env(env)
f95.add_to_env(env)
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f77'
def exists(env):
return env.Detect(compilers)

View file

@ -1,132 +0,0 @@
"""engine.SCons.Tool.f90
Tool-specific initialization for the generic Posix f90 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/f90.py 2523 2007/12/12 09:37:41 knight"
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
import fortran
compilers = ['f90']
#
F90Suffixes = ['.f90']
F90PPSuffixes = []
if SCons.Util.case_sensitive_suffixes('.f90', '.F90'):
F90PPSuffixes.append('.F90')
else:
F90Suffixes.append('.F90')
#
F90Scan = SCons.Scanner.Fortran.FortranScan("F90PATH")
for suffix in F90Suffixes + F90PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F90Scan)
del suffix
#
fVLG = fortran.VariableListGenerator
F90Generator = fVLG('F90', 'FORTRAN', '_FORTRAND')
F90FlagsGenerator = fVLG('F90FLAGS', 'FORTRANFLAGS')
F90CommandGenerator = fVLG('F90COM', 'FORTRANCOM', '_F90COMD')
F90CommandStrGenerator = fVLG('F90COMSTR', 'FORTRANCOMSTR', '_F90COMSTRD')
F90PPCommandGenerator = fVLG('F90PPCOM', 'FORTRANPPCOM', '_F90PPCOMD')
F90PPCommandStrGenerator = fVLG('F90PPCOMSTR', 'FORTRANPPCOMSTR', '_F90PPCOMSTRD')
ShF90Generator = fVLG('SHF90', 'SHFORTRAN', 'F90', 'FORTRAN', '_FORTRAND')
ShF90FlagsGenerator = fVLG('SHF90FLAGS', 'SHFORTRANFLAGS')
ShF90CommandGenerator = fVLG('SHF90COM', 'SHFORTRANCOM', '_SHF90COMD')
ShF90CommandStrGenerator = fVLG('SHF90COMSTR', 'SHFORTRANCOMSTR', '_SHF90COMSTRD')
ShF90PPCommandGenerator = fVLG('SHF90PPCOM', 'SHFORTRANPPCOM', '_SHF90PPCOMD')
ShF90PPCommandStrGenerator = fVLG('SHF90PPCOMSTR', 'SHFORTRANPPCOMSTR', '_SHF90PPCOMSTRD')
del fVLG
#
F90Action = SCons.Action.Action('$_F90COMG ', '$_F90COMSTRG')
F90PPAction = SCons.Action.Action('$_F90PPCOMG ', '$_F90PPCOMSTRG')
ShF90Action = SCons.Action.Action('$_SHF90COMG ', '$_SHF90COMSTRG')
ShF90PPAction = SCons.Action.Action('$_SHF90PPCOMG ', '$_SHF90PPCOMSTRG')
def add_to_env(env):
"""Add Builders and construction variables for f90 to an Environment."""
env.AppendUnique(FORTRANSUFFIXES = F90Suffixes + F90PPSuffixes)
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in F90Suffixes:
static_obj.add_action(suffix, F90Action)
shared_obj.add_action(suffix, ShF90Action)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
for suffix in F90PPSuffixes:
static_obj.add_action(suffix, F90PPAction)
shared_obj.add_action(suffix, ShF90PPAction)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
env['_F90G'] = F90Generator
env['_F90FLAGSG'] = F90FlagsGenerator
env['_F90COMG'] = F90CommandGenerator
env['_F90COMSTRG'] = F90CommandStrGenerator
env['_F90PPCOMG'] = F90PPCommandGenerator
env['_F90PPCOMSTRG'] = F90PPCommandStrGenerator
env['_SHF90G'] = ShF90Generator
env['_SHF90FLAGSG'] = ShF90FlagsGenerator
env['_SHF90COMG'] = ShF90CommandGenerator
env['_SHF90COMSTRG'] = ShF90CommandStrGenerator
env['_SHF90PPCOMG'] = ShF90PPCommandGenerator
env['_SHF90PPCOMSTRG'] = ShF90PPCommandStrGenerator
env['_F90INCFLAGS'] = '$( ${_concat(INCPREFIX, F90PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_F90COMD'] = '$_F90G -o $TARGET -c $_F90FLAGSG $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_F90PPCOMD'] = '$_F90G -o $TARGET -c $_F90FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHF90COMD'] = '$_SHF90G -o $TARGET -c $_SHF90FLAGSG $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHF90PPCOMD'] = '$_SHF90G -o $TARGET -c $_SHF90FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F90INCFLAGS $_FORTRANMODFLAG $SOURCES'
def generate(env):
fortran.add_to_env(env)
import f77
f77.add_to_env(env)
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f90'
def exists(env):
return env.Detect(compilers)

View file

@ -1,135 +0,0 @@
"""engine.SCons.Tool.f95
Tool-specific initialization for the generic Posix f95 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/f95.py 2523 2007/12/12 09:37:41 knight"
import SCons.Defaults
import SCons.Tool
import SCons.Util
import fortran
compilers = ['f95']
#
F95Suffixes = ['.f95']
F95PPSuffixes = []
if SCons.Util.case_sensitive_suffixes('.f95', '.F95'):
F95PPSuffixes.append('.F95')
else:
F95Suffixes.append('.F95')
#
F95Scan = SCons.Scanner.Fortran.FortranScan("F95PATH")
for suffix in F95Suffixes + F95PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F95Scan)
del suffix
#
fVLG = fortran.VariableListGenerator
F95Generator = fVLG('F95', 'FORTRAN', '_FORTRAND')
F95FlagsGenerator = fVLG('F95FLAGS', 'FORTRANFLAGS')
F95CommandGenerator = fVLG('F95COM', 'FORTRANCOM', '_F95COMD')
F95CommandStrGenerator = fVLG('F95COMSTR', 'FORTRANCOMSTR', '_F95COMSTRD')
F95PPCommandGenerator = fVLG('F95PPCOM', 'FORTRANPPCOM', '_F95PPCOMD')
F95PPCommandStrGenerator = fVLG('F95PPCOMSTR', 'FORTRANPPCOMSTR', '_F95PPCOMSTRD')
ShF95Generator = fVLG('SHF95', 'SHFORTRAN', 'F95', 'FORTRAN', '_FORTRAND')
ShF95FlagsGenerator = fVLG('SHF95FLAGS', 'SHFORTRANFLAGS')
ShF95CommandGenerator = fVLG('SHF95COM', 'SHFORTRANCOM', '_SHF95COMD')
ShF95CommandStrGenerator = fVLG('SHF95COMSTR', 'SHFORTRANCOMSTR', '_SHF95COMSTRD')
ShF95PPCommandGenerator = fVLG('SHF95PPCOM', 'SHFORTRANPPCOM', '_SHF95PPCOMD')
ShF95PPCommandStrGenerator = fVLG('SHF95PPCOMSTR', 'SHFORTRANPPCOMSTR', '_SHF95PPCOMSTRD')
del fVLG
#
F95Action = SCons.Action.Action('$_F95COMG ', '$_F95COMSTRG')
F95PPAction = SCons.Action.Action('$_F95PPCOMG ', '$_F95PPCOMSTRG')
ShF95Action = SCons.Action.Action('$_SHF95COMG ', '$_SHF95COMSTRG')
ShF95PPAction = SCons.Action.Action('$_SHF95PPCOMG ', '$_SHF95PPCOMSTRG')
def add_to_env(env):
"""Add Builders and construction variables for f95 to an Environment."""
env.AppendUnique(FORTRANSUFFIXES = F95Suffixes + F95PPSuffixes)
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in F95Suffixes:
static_obj.add_action(suffix, F95Action)
shared_obj.add_action(suffix, ShF95Action)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
for suffix in F95PPSuffixes:
static_obj.add_action(suffix, F95PPAction)
shared_obj.add_action(suffix, ShF95PPAction)
static_obj.add_emitter(suffix, fortran.FortranEmitter)
shared_obj.add_emitter(suffix, fortran.ShFortranEmitter)
env['_F95G'] = F95Generator
env['_F95FLAGSG'] = F95FlagsGenerator
env['_F95COMG'] = F95CommandGenerator
env['_F95COMSTRG'] = F95CommandStrGenerator
env['_F95PPCOMG'] = F95PPCommandGenerator
env['_F95PPCOMSTRG'] = F95PPCommandStrGenerator
env['_SHF95G'] = ShF95Generator
env['_SHF95FLAGSG'] = ShF95FlagsGenerator
env['_SHF95COMG'] = ShF95CommandGenerator
env['_SHF95COMSTRG'] = ShF95CommandStrGenerator
env['_SHF95PPCOMG'] = ShF95PPCommandGenerator
env['_SHF95PPCOMSTRG'] = ShF95PPCommandStrGenerator
env['_F95INCFLAGS'] = '$( ${_concat(INCPREFIX, F95PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_F95COMD'] = '$_F95G -o $TARGET -c $_F95FLAGSG $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_F95PPCOMD'] = '$_F95G -o $TARGET -c $_F95FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHF95COMD'] = '$_SHF95G -o $TARGET -c $_SHF95FLAGSG $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHF95PPCOMD'] = '$_SHF95G -o $TARGET -c $_SHF95FLAGSG $CPPFLAGS $_CPPDEFFLAGS $_F95INCFLAGS $_FORTRANMODFLAG $SOURCES'
def generate(env):
fortran.add_to_env(env)
import f77
f77.add_to_env(env)
import f90
f90.add_to_env(env)
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f95'
def exists(env):
return env.Detect(compilers)

View file

@ -1,186 +0,0 @@
"""SCons.Tool.fortran
Tool-specific initialization for a generic Posix f77/f90 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/fortran.py 2523 2007/12/12 09:37:41 knight"
import re
import string
import SCons.Action
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
compilers = ['f95', 'f90', 'f77']
#
# Not yet sure how to deal with fortran pre-processor functions.
# Different compilers do this differently in modern fortran. Some still
# rely on the c pre-processor, some (like cvf, ivf) have their own
# pre-processor technology and use intermediary suffixes (.i90)
#
FortranSuffixes = [".f", ".for", ".ftn", ]
FortranPPSuffixes = ['.fpp', '.FPP']
upper_case = [".F", ".FOR", ".FTN"]
if SCons.Util.case_sensitive_suffixes('.f', '.F'):
FortranPPSuffixes.extend(upper_case)
else:
FortranSuffixes.extend(upper_case)
#
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):
node = source[0].rfile()
if not node.exists() and not node.is_derived():
print "Could not locate " + str(node.name)
return ([], [])
mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)"""
cre = re.compile(mod_regex,re.M)
# Retrieve all USE'd module names
modules = cre.findall(node.get_contents())
# Remove unique items from the list
modules = SCons.Util.unique(modules)
# Convert module name to a .mod filename
suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source)
moddir = env.subst('$FORTRANMODDIR', target=target, source=source)
modules = map(lambda x, s=suffix: string.lower(x) + s, modules)
for m in modules:
target.append(env.fs.File(m, moddir))
return (target, source)
def FortranEmitter(target, source, env):
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.StaticObjectEmitter(target, source, env)
def ShFortranEmitter(target, source, env):
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.SharedObjectEmitter(target, source, env)
class VariableListGenerator:
def __init__(self, *variablelist):
self.variablelist = variablelist
def __call__(self, env, target, source, for_signature=0):
for v in self.variablelist:
try: return env[v]
except KeyError: pass
return ''
#
FortranGenerator = VariableListGenerator('FORTRAN', 'F77', '_FORTRAND')
FortranFlagsGenerator = VariableListGenerator('FORTRANFLAGS', 'F77FLAGS')
FortranCommandGenerator = VariableListGenerator('FORTRANCOM', 'F77COM', '_FORTRANCOMD')
FortranCommandStrGenerator = VariableListGenerator('FORTRANCOMSTR', 'F77COMSTR', '_FORTRANCOMSTRD')
FortranPPCommandGenerator = VariableListGenerator('FORTRANPPCOM', 'F77PPCOM', '_FORTRANPPCOMD')
FortranPPCommandStrGenerator = VariableListGenerator('FORTRANPPCOMSTR', 'F77PPCOMSTR', '_FORTRANPPCOMSTRD')
ShFortranGenerator = VariableListGenerator('SHFORTRAN', 'SHF77', 'FORTRAN', 'F77', '_FORTRAND')
ShFortranFlagsGenerator = VariableListGenerator('SHFORTRANFLAGS', 'SHF77FLAGS')
ShFortranCommandGenerator = VariableListGenerator('SHFORTRANCOM', 'SHF77COM', '_SHFORTRANCOMD')
ShFortranCommandStrGenerator = VariableListGenerator('SHFORTRANCOMSTR', 'SHF77COMSTR', '_SHFORTRANCOMSTRD')
ShFortranPPCommandGenerator = VariableListGenerator('SHFORTRANPPCOM', 'SHF77PPCOM', '_SHFORTRANPPCOMD')
ShFortranPPCommandStrGenerator = VariableListGenerator('SHFORTRANPPCOMSTR', 'SHF77PPCOMSTR', '_SHFORTRANPPCOMSTRD')
#
FortranAction = SCons.Action.Action('$_FORTRANCOMG ', '$_FORTRANCOMSTRG')
FortranPPAction = SCons.Action.Action('$_FORTRANPPCOMG ', '$_FORTRANPPCOMSTRG')
ShFortranAction = SCons.Action.Action('$_SHFORTRANCOMG ', '$_SHFORTRANCOMSTRG')
ShFortranPPAction = SCons.Action.Action('$_SHFORTRANPPCOMG ', '$_SHFORTRANPPCOMSTRG')
def add_to_env(env):
"""Add Builders and construction variables for Fortran to an Environment."""
env['_FORTRANG'] = FortranGenerator
env['_FORTRANFLAGSG'] = FortranFlagsGenerator
env['_FORTRANCOMG'] = FortranCommandGenerator
env['_FORTRANCOMSTRG'] = FortranCommandStrGenerator
env['_FORTRANPPCOMG'] = FortranPPCommandGenerator
env['_FORTRANPPCOMSTRG'] = FortranPPCommandStrGenerator
env['_SHFORTRANG'] = ShFortranGenerator
env['_SHFORTRANFLAGSG'] = ShFortranFlagsGenerator
env['_SHFORTRANCOMG'] = ShFortranCommandGenerator
env['_SHFORTRANCOMSTRG'] = ShFortranCommandStrGenerator
env['_SHFORTRANPPCOMG'] = ShFortranPPCommandGenerator
env['_SHFORTRANPPCOMSTRG'] = ShFortranPPCommandStrGenerator
env['_FORTRANINCFLAGS'] = '$( ${_concat(INCPREFIX, FORTRANPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
env['FORTRANMODDIR'] = '' # where the compiler should place .mod files
env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs)} $)'
env.AppendUnique(FORTRANSUFFIXES = FortranSuffixes + FortranPPSuffixes)
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in FortranSuffixes:
static_obj.add_action(suffix, FortranAction)
shared_obj.add_action(suffix, ShFortranAction)
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
for suffix in FortranPPSuffixes:
static_obj.add_action(suffix, FortranPPAction)
shared_obj.add_action(suffix, ShFortranPPAction)
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
env['_FORTRANCOMD'] = '$_FORTRANG -o $TARGET -c $_FORTRANFLAGSG $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_FORTRANPPCOMD'] = '$_FORTRANG -o $TARGET -c $_FORTRANFLAGSG $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHFORTRANCOMD'] = '$_SHFORTRANG -o $TARGET -c $_SHFORTRANFLAGSG $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES'
env['_SHFORTRANPPCOMD'] = '$_SHFORTRANG -o $TARGET -c $_SHFORTRANFLAGSG $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS $_FORTRANMODFLAG $SOURCES'
def generate(env):
import f77
import f90
import f95
f77.add_to_env(env)
f90.add_to_env(env)
f95.add_to_env(env)
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f77'
def exists(env):
return env.Detect(compilers)

View file

@ -1,268 +0,0 @@
"""SCons.Tool.tex
Tool-specific initialization for TeX.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/tex.py 2523 2007/12/12 09:37:41 knight"
import os.path
import re
import string
import SCons.Action
import SCons.Node
import SCons.Node.FS
import SCons.Util
warning_rerun_re = re.compile("^LaTeX Warning:.*Rerun", re.MULTILINE)
rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct"
rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE)
undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)'
undefined_references_re = re.compile(undefined_references_str, re.MULTILINE)
openout_aux_re = re.compile(r"\\openout.*`(.*\.aux)'")
openout_re = re.compile(r"\\openout.*`(.*)'")
makeindex_re = re.compile(r"^[^%]*\\makeindex", re.MULTILINE)
tableofcontents_re = re.compile(r"^[^%]*\\tableofcontents", re.MULTILINE)
bibliography_re = re.compile(r"^[^%]*\\bibliography", re.MULTILINE)
# An Action sufficient to build any generic tex file.
TeXAction = None
# An action to build a latex file. This action might be needed more
# than once if we are dealing with labels and bibtex.
LaTeXAction = None
# An action to run BibTeX on a file.
BibTeXAction = None
# An action to run MakeIndex on a file.
MakeIndexAction = None
def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None):
"""A builder for LaTeX files that checks the output in the aux file
and decides how many times to use LaTeXAction, and BibTeXAction."""
basename = SCons.Util.splitext(str(source[0]))[0]
basedir = os.path.split(str(source[0]))[0]
# Notice that all the filenames are not prefixed with the basedir.
# That's because the *COM variables have the cd command in the prolog.
bblfilename = basename + '.bbl'
bblContents = ""
if os.path.exists(bblfilename):
bblContents = open(bblfilename, "rb").read()
idxfilename = basename + '.idx'
idxContents = ""
if os.path.exists(idxfilename):
idxContents = open(idxfilename, "rb").read()
tocfilename = basename + '.toc'
tocContents = ""
if os.path.exists(tocfilename):
tocContents = open(tocfilename, "rb").read()
# Run LaTeX once to generate a new aux file.
XXXLaTeXAction(target, source, env)
# Decide if various things need to be run, or run again. We check
# for the existence of files before opening them--even ones like the
# aux file that TeX always creates--to make it possible to write tests
# with stubs that don't necessarily generate all of the same files.
# Read the log file to find all .aux files
logfilename = basename + '.log'
auxfiles = []
if os.path.exists(logfilename):
content = open(logfilename, "rb").read()
auxfiles = openout_aux_re.findall(content)
# Now decide if bibtex will need to be run.
for auxfilename in auxfiles:
if os.path.exists(os.path.join(basedir, auxfilename)):
content = open(os.path.join(basedir, auxfilename), "rb").read()
if string.find(content, "bibdata") != -1:
bibfile = env.fs.File(basename)
BibTeXAction(bibfile, bibfile, env)
break
must_rerun_latex = 0
# Now decide if latex will need to be run again due to table of contents.
if os.path.exists(tocfilename) and tocContents != open(tocfilename, "rb").read():
must_rerun_latex = 1
# Now decide if latex will need to be run again due to bibliography.
if os.path.exists(bblfilename) and bblContents != open(bblfilename, "rb").read():
must_rerun_latex = 1
# Now decide if latex will need to be run again due to index.
if os.path.exists(idxfilename) and idxContents != open(idxfilename, "rb").read():
# We must run makeindex
idxfile = env.fs.File(basename)
MakeIndexAction(idxfile, idxfile, env)
must_rerun_latex = 1
if must_rerun_latex == 1:
XXXLaTeXAction(target, source, env)
# Now decide if latex needs to be run yet again to resolve warnings.
logfilename = basename + '.log'
for _ in range(int(env.subst('$LATEXRETRIES'))):
if not os.path.exists(logfilename):
break
content = open(logfilename, "rb").read()
if not warning_rerun_re.search(content) and \
not rerun_citations_re.search(content) and \
not undefined_references_re.search(content):
break
XXXLaTeXAction(target, source, env)
return 0
def LaTeXAuxAction(target = None, source= None, env=None):
InternalLaTeXAuxAction( LaTeXAction, target, source, env )
LaTeX_re = re.compile("\\\\document(style|class)")
def is_LaTeX(flist):
# Scan a file list to decide if it's TeX- or LaTeX-flavored.
for f in flist:
content = f.get_contents()
if LaTeX_re.search(content):
return 1
return 0
def TeXLaTeXFunction(target = None, source= None, env=None):
"""A builder for TeX and LaTeX that scans the source file to
decide the "flavor" of the source and then executes the appropriate
program."""
if is_LaTeX(source):
LaTeXAuxAction(target,source,env)
else:
TeXAction(target,source,env)
return 0
def tex_emitter(target, source, env):
base = SCons.Util.splitext(str(source[0]))[0]
target.append(base + '.aux')
env.Precious(base + '.aux')
target.append(base + '.log')
for f in source:
content = f.get_contents()
if tableofcontents_re.search(content):
target.append(base + '.toc')
env.Precious(base + '.toc')
if makeindex_re.search(content):
target.append(base + '.ilg')
target.append(base + '.ind')
target.append(base + '.idx')
env.Precious(base + '.idx')
if bibliography_re.search(content):
target.append(base + '.bbl')
env.Precious(base + '.bbl')
target.append(base + '.blg')
# read log file to get all output file (include .aux files)
logfilename = base + '.log'
dir, base_nodir = os.path.split(base)
if os.path.exists(logfilename):
content = open(logfilename, "rb").read()
out_files = openout_re.findall(content)
out_files = filter(lambda f, b=base_nodir+'.aux': f != b, out_files)
if dir != '':
out_files = map(lambda f, d=dir: d+os.sep+f, out_files)
target.extend(out_files)
for f in out_files:
env.Precious( f )
return (target, source)
TeXLaTeXAction = None
def generate(env):
"""Add Builders and construction variables for TeX to an Environment."""
# A generic tex file Action, sufficient for all tex files.
global TeXAction
if TeXAction is None:
TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR")
# An Action to build a latex file. This might be needed more
# than once if we are dealing with labels and bibtex.
global LaTeXAction
if LaTeXAction is None:
LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR")
# Define an action to run BibTeX on a file.
global BibTeXAction
if BibTeXAction is None:
BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR")
# Define an action to run MakeIndex on a file.
global MakeIndexAction
if MakeIndexAction is None:
MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR")
global TeXLaTeXAction
if TeXLaTeXAction is None:
TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, strfunction=None)
import dvi
dvi.generate(env)
bld = env['BUILDERS']['DVI']
bld.add_action('.tex', TeXLaTeXAction)
bld.add_emitter('.tex', tex_emitter)
env['TEX'] = 'tex'
env['TEXFLAGS'] = SCons.Util.CLVar('')
env['TEXCOM'] = 'cd ${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}'
# Duplicate from latex.py. If latex.py goes away, then this is still OK.
env['LATEX'] = 'latex'
env['LATEXFLAGS'] = SCons.Util.CLVar('')
env['LATEXCOM'] = 'cd ${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}'
env['LATEXRETRIES'] = 3
env['BIBTEX'] = 'bibtex'
env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
env['BIBTEXCOM'] = 'cd ${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}'
env['MAKEINDEX'] = 'makeindex'
env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
env['MAKEINDEXCOM'] = 'cd ${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}'
def exists(env):
return env.Detect('tex')

View file

@ -1,13 +0,0 @@
Metadata-Version: 1.0
Name: scons
Version: 0.97.0d20071212
Summary: 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.
Home-page: http://www.scons.org/
Author: Steven Knight
Author-email: knight@baldmt.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View file

@ -30,9 +30,9 @@ other modules:
a pre-substitution command for debugging purposes. a pre-substitution command for debugging purposes.
get_contents() get_contents()
Fetches the "contents" of an Action for signature calculation. Fetches the "contents" of an Action for signature calculation
This is what gets MD5 checksumm'ed to decide if a target needs plus the varlist. This is what gets MD5 checksummed to decide
to be rebuilt because its action changed. if a target needs to be rebuilt because its action changed.
genstring() genstring()
Returns a string representation of the Action *without* Returns a string representation of the Action *without*
@ -57,6 +57,10 @@ this module:
pre-substitution representations, and *then* execute an action pre-substitution representations, and *then* execute an action
without worrying about the specific Actions involved. without worrying about the specific Actions involved.
get_presig()
Fetches the "contents" of a subclass for signature calculation.
The varlist is added to this to produce the Action's contents.
strfunction() strfunction()
Returns a substituted string representation of the Action. Returns a substituted string representation of the Action.
This is used by the _ActionAction.show() command to display the This is used by the _ActionAction.show() command to display the
@ -72,8 +76,7 @@ way for wrapping up the functions.
""" """
# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -93,32 +96,33 @@ way for wrapping up the functions.
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
__revision__ = "src/engine/SCons/Action.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Action.py 3842 2008/12/20 22:59:52 scons"
import cPickle
import dis import dis
import os import os
import os.path
import string import string
import sys import sys
import subprocess
from SCons.Debug import logInstanceCreation from SCons.Debug import logInstanceCreation
import SCons.Errors import SCons.Errors
import SCons.Executor import SCons.Executor
import SCons.Util import SCons.Util
import SCons.Subst
class _Null: # we use these a lot, so try to optimize them
is_String = SCons.Util.is_String
is_List = SCons.Util.is_List
class _null:
pass pass
_null = _Null
print_actions = 1 print_actions = 1
execute_actions = 1 execute_actions = 1
print_actions_presub = 0 print_actions_presub = 0
default_ENV = None
def rfile(n): def rfile(n):
try: try:
return n.rfile() return n.rfile()
@ -150,6 +154,141 @@ else:
i = i+1 i = i+1
return string.join(result, '') return string.join(result, '')
def _callable_contents(obj):
"""Return the signature contents of a callable Python object.
"""
try:
# Test if obj is a method.
return _function_contents(obj.im_func)
except AttributeError:
try:
# Test if obj is a callable object.
return _function_contents(obj.__call__.im_func)
except AttributeError:
try:
# Test if obj is a code object.
return _code_contents(obj)
except AttributeError:
# Test if obj is a function object.
return _function_contents(obj)
def _object_contents(obj):
"""Return the signature contents of any Python object.
We have to handle the case where object contains a code object
since it can be pickled directly.
"""
try:
# Test if obj is a method.
return _function_contents(obj.im_func)
except AttributeError:
try:
# Test if obj is a callable object.
return _function_contents(obj.__call__.im_func)
except AttributeError:
try:
# Test if obj is a code object.
return _code_contents(obj)
except AttributeError:
try:
# Test if obj is a function object.
return _function_contents(obj)
except AttributeError:
# Should be a pickable Python object.
try:
return cPickle.dumps(obj)
except (cPickle.PicklingError, TypeError):
# This is weird, but it seems that nested classes
# are unpickable. The Python docs say it should
# always be a PicklingError, but some Python
# versions seem to return TypeError. Just do
# the best we can.
return str(obj)
def _code_contents(code):
"""Return the signature contents of a code object.
By providing direct access to the code object of the
function, Python makes this extremely easy. Hooray!
Unfortunately, older versions of Python include line
number indications in the compiled byte code. Boo!
So we remove the line number byte codes to prevent
recompilations from moving a Python function.
"""
contents = []
# The code contents depends on the number of local variables
# but not their actual names.
contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
try:
contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
except AttributeError:
# Older versions of Python do not support closures.
contents.append(",0,0")
# The code contents depends on any constants accessed by the
# function. Note that we have to call _object_contents on each
# constants because the code object of nested functions can
# show-up among the constants.
#
# Note that we also always ignore the first entry of co_consts
# which contains the function doc string. We assume that the
# function does not access its doc string.
contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
# The code contents depends on the variable names used to
# accessed global variable, as changing the variable name changes
# the variable actually accessed and therefore changes the
# function result.
contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
# The code contents depends on its actual code!!!
contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
return string.join(contents, '')
def _function_contents(func):
"""Return the signature contents of a function."""
contents = [_code_contents(func.func_code)]
# The function contents depends on the value of defaults arguments
if func.func_defaults:
contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
else:
contents.append(',()')
# The function contents depends on the closure captured cell values.
try:
closure = func.func_closure or []
except AttributeError:
# Older versions of Python do not support closures.
closure = []
#xxx = [_object_contents(x.cell_contents) for x in closure]
try:
xxx = map(lambda x: _object_contents(x.cell_contents), closure)
except AttributeError:
xxx = []
contents.append(',(' + string.join(xxx, ',') + ')')
return string.join(contents, '')
def _actionAppend(act1, act2): def _actionAppend(act1, act2):
# This function knows how to slap two actions together. # This function knows how to slap two actions together.
# Mainly, it handles ListActions by concatenating into # Mainly, it handles ListActions by concatenating into
@ -169,7 +308,34 @@ def _actionAppend(act1, act2):
else: else:
return ListAction([ a1, a2 ]) return ListAction([ a1, a2 ])
def _do_create_action(act, *args, **kw): def _do_create_keywords(args, kw):
"""This converts any arguments after the action argument into
their equivalent keywords and adds them to the kw argument.
"""
v = kw.get('varlist', ())
# prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
if is_String(v): v = (v,)
kw['varlist'] = tuple(v)
if args:
# turn positional args into equivalent keywords
cmdstrfunc = args[0]
if cmdstrfunc is None or is_String(cmdstrfunc):
kw['cmdstr'] = cmdstrfunc
elif callable(cmdstrfunc):
kw['strfunction'] = cmdstrfunc
else:
raise SCons.Errors.UserError(
'Invalid command display variable type. '
'You must either pass a string or a callback which '
'accepts (target, source, env) as parameters.')
if len(args) > 1:
kw['varlist'] = args[1:] + kw['varlist']
if kw.get('strfunction', _null) is not _null \
and kw.get('cmdstr', _null) is not _null:
raise SCons.Errors.UserError(
'Cannot have both strfunction and cmdstr args to Action()')
def _do_create_action(act, kw):
"""This is the actual "implementation" for the """This is the actual "implementation" for the
Action factory method, below. This handles the Action factory method, below. This handles the
fact that passing lists to Action() itself has fact that passing lists to Action() itself has
@ -182,8 +348,11 @@ def _do_create_action(act, *args, **kw):
if isinstance(act, ActionBase): if isinstance(act, ActionBase):
return act return act
if SCons.Util.is_List(act):
return apply(CommandAction, (act,)+args, kw) if is_List(act):
#TODO(1.5) return CommandAction(act, **kw)
return apply(CommandAction, (act,), kw)
if callable(act): if callable(act):
try: try:
gen = kw['generator'] gen = kw['generator']
@ -194,8 +363,9 @@ def _do_create_action(act, *args, **kw):
action_type = CommandGeneratorAction action_type = CommandGeneratorAction
else: else:
action_type = FunctionAction action_type = FunctionAction
return apply(action_type, (act,)+args, kw) return action_type(act, kw)
if SCons.Util.is_String(act):
if is_String(act):
var=SCons.Util.get_environment_var(act) var=SCons.Util.get_environment_var(act)
if var: if var:
# This looks like a string that is purely an Environment # This looks like a string that is purely an Environment
@ -204,30 +374,37 @@ def _do_create_action(act, *args, **kw):
# of that Environment variable, so a user could put something # of that Environment variable, so a user could put something
# like a function or a CommandGenerator in that variable # like a function or a CommandGenerator in that variable
# instead of a string. # instead of a string.
return apply(LazyAction, (var,)+args, kw) return LazyAction(var, kw)
commands = string.split(str(act), '\n') commands = string.split(str(act), '\n')
if len(commands) == 1: if len(commands) == 1:
return apply(CommandAction, (commands[0],)+args, kw) #TODO(1.5) return CommandAction(commands[0], **kw)
else: return apply(CommandAction, (commands[0],), kw)
listCmdActions = map(lambda x, args=args, kw=kw: # The list of string commands may include a LazyAction, so we
apply(CommandAction, (x,)+args, kw), # reprocess them via _do_create_list_action.
commands) return _do_create_list_action(commands, kw)
return ListAction(listCmdActions)
return None return None
def Action(act, *args, **kw): def _do_create_list_action(act, kw):
"""A factory for action objects.""" """A factory for list actions. Convert the input list into Actions
if SCons.Util.is_List(act): and then wrap them in a ListAction."""
acts = map(lambda a, args=args, kw=kw: acts = []
apply(_do_create_action, (a,)+args, kw), for a in act:
act) aa = _do_create_action(a, kw)
acts = filter(None, acts) if aa is not None: acts.append(aa)
if len(acts) == 1: if not acts:
return None
elif len(acts) == 1:
return acts[0] return acts[0]
else: else:
return ListAction(acts) return ListAction(acts)
else:
return apply(_do_create_action, (act,)+args, kw) def Action(act, *args, **kw):
"""A factory for action objects."""
# Really simple: the _do_create_* routines do the heavy lifting.
_do_create_keywords(args, kw)
if is_List(act):
return _do_create_list_action(act, kw)
return _do_create_action(act, kw)
class ActionBase: class ActionBase:
"""Base class for all types of action objects that can be held by """Base class for all types of action objects that can be held by
@ -240,6 +417,17 @@ class ActionBase:
def genstring(self, target, source, env): def genstring(self, target, source, env):
return str(self) return str(self)
def get_contents(self, target, source, env):
result = [ self.get_presig(target, source, env) ]
# This should never happen, as the Action() factory should wrap
# the varlist, but just in case an action is created directly,
# we duplicate this check here.
vl = self.varlist
if is_String(vl): vl = (vl,)
for v in vl:
result.append(env.subst('${'+v+'}'))
return string.join(result, '')
def __add__(self, other): def __add__(self, other):
return _actionAppend(self, other) return _actionAppend(self, other)
@ -265,9 +453,16 @@ class ActionBase:
class _ActionAction(ActionBase): class _ActionAction(ActionBase):
"""Base class for actions that create output objects.""" """Base class for actions that create output objects."""
def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw): def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
if not strfunction is _null: presub=_null, chdir=None, exitstatfunc=None,
**kw):
self.cmdstr = cmdstr
if strfunction is not _null:
if strfunction is None:
self.cmdstr = None
else:
self.strfunction = strfunction self.strfunction = strfunction
self.varlist = varlist
self.presub = presub self.presub = presub
self.chdir = chdir self.chdir = chdir
if not exitstatfunc: if not exitstatfunc:
@ -283,16 +478,16 @@ class _ActionAction(ActionBase):
show=_null, show=_null,
execute=_null, execute=_null,
chdir=_null): chdir=_null):
if not SCons.Util.is_List(target): if not is_List(target):
target = [target] target = [target]
if not SCons.Util.is_List(source): if not is_List(source):
source = [source] source = [source]
if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
if presub is _null: if presub is _null:
presub = self.presub presub = self.presub
if presub is _null: if presub is _null:
presub = print_actions_presub presub = print_actions_presub
if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
if show is _null: show = print_actions if show is _null: show = print_actions
if execute is _null: execute = execute_actions if execute is _null: execute = execute_actions
if chdir is _null: chdir = self.chdir if chdir is _null: chdir = self.chdir
@ -302,19 +497,19 @@ class _ActionAction(ActionBase):
try: try:
chdir = str(chdir.abspath) chdir = str(chdir.abspath)
except AttributeError: except AttributeError:
if not SCons.Util.is_String(chdir): if not is_String(chdir):
chdir = str(target[0].dir) chdir = str(target[0].dir)
if presub: if presub:
t = string.join(map(str, target), ' and ') t = string.join(map(str, target), ' and ')
l = string.join(self.presub_lines(env), '\n ') l = string.join(self.presub_lines(env), '\n ')
out = "Building %s with action:\n %s\n" % (t, l) out = "Building %s with action:\n %s\n" % (t, l)
sys.stdout.write(out) sys.stdout.write(out)
s = None cmd = None
if show and self.strfunction: if show and self.strfunction:
s = self.strfunction(target, source, env) cmd = self.strfunction(target, source, env)
if s: if cmd:
if chdir: if chdir:
s = ('os.chdir(%s)\n' % repr(chdir)) + s cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
try: try:
get = env.get get = env.get
except AttributeError: except AttributeError:
@ -323,7 +518,7 @@ class _ActionAction(ActionBase):
print_func = get('PRINT_CMD_LINE_FUNC') print_func = get('PRINT_CMD_LINE_FUNC')
if not print_func: if not print_func:
print_func = self.print_cmd_line print_func = self.print_cmd_line
print_func(s, target, source, env) print_func(cmd, target, source, env)
stat = 0 stat = 0
if execute: if execute:
if chdir: if chdir:
@ -341,7 +536,7 @@ class _ActionAction(ActionBase):
finally: finally:
if save_cwd: if save_cwd:
os.chdir(save_cwd) os.chdir(save_cwd)
if s and save_cwd: if cmd and save_cwd:
print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
return stat return stat
@ -357,9 +552,87 @@ def _string_from_cmd_list(cmd_list):
cl.append(arg) cl.append(arg)
return string.join(cl) return string.join(cl)
# A fiddlin' little function that has an 'import SCons.Environment' which
# can't be moved to the top level without creating an import loop. Since
# this import creates a local variable named 'SCons', it blocks access to
# the global variable, so we move it here to prevent complaints about local
# variables being used uninitialized.
default_ENV = None
def get_default_ENV(env):
global default_ENV
try:
return env['ENV']
except KeyError:
if not default_ENV:
import SCons.Environment
# This is a hideously expensive way to get a default shell
# environment. What it really should do is run the platform
# setup to get the default ENV. Fortunately, it's incredibly
# rare for an Environment not to have a shell environment, so
# we're not going to worry about it overmuch.
default_ENV = SCons.Environment.Environment()['ENV']
return default_ENV
# This function is still in draft mode. We're going to need something like
# it in the long run as more and more places use subprocess, but I'm sure
# it'll have to be tweaked to get the full desired functionality.
# one special arg (so far?), 'error', to tell what to do with exceptions.
def _subproc(env, cmd, error = 'ignore', **kw):
"""Do common setup for a subprocess.Popen() call"""
# allow std{in,out,err} to be "'devnull'"
io = kw.get('stdin')
if is_String(io) and io == 'devnull':
kw['stdin'] = open(os.devnull)
io = kw.get('stdout')
if is_String(io) and io == 'devnull':
kw['stdout'] = open(os.devnull, 'w')
io = kw.get('stderr')
if is_String(io) and io == 'devnull':
kw['stderr'] = open(os.devnull, 'w')
# Figure out what shell environment to use
ENV = kw.get('env', None)
if ENV is None: ENV = get_default_ENV(env)
# Ensure that the ENV values are all strings:
new_env = {}
for key, value in ENV.items():
if is_List(value):
# If the value is a list, then we assume it is a path list,
# because that's a pretty common list-like value to stick
# in an environment variable:
value = SCons.Util.flatten_sequence(value)
new_env[key] = string.join(map(str, value), os.pathsep)
else:
# It's either a string or something else. If it's a string,
# we still want to call str() because it might be a *Unicode*
# string, which makes subprocess.Popen() gag. If it isn't a
# string or a list, then we just coerce it to a string, which
# is the proper way to handle Dir and File instances and will
# produce something reasonable for just about everything else:
new_env[key] = str(value)
kw['env'] = new_env
try:
#FUTURE return subprocess.Popen(cmd, **kw)
return apply(subprocess.Popen, (cmd,), kw)
except EnvironmentError, e:
if error == 'raise': raise
# return a dummy Popen instance that only returns error
class dummyPopen:
def __init__(self, e): self.exception = e
def communicate(self): return ('','')
def wait(self): return -self.exception.errno
stdin = None
class f:
def read(self): return ''
def readline(self): return ''
stdout = stderr = f()
return dummyPopen(e)
class CommandAction(_ActionAction): class CommandAction(_ActionAction):
"""Class for command-execution actions.""" """Class for command-execution actions."""
def __init__(self, cmd, cmdstr=None, *args, **kw): def __init__(self, cmd, **kw):
# Cmd can actually be a list or a single item; if it's a # Cmd can actually be a list or a single item; if it's a
# single item it should be the command string to execute; if a # single item it should be the command string to execute; if a
# list then it should be the words of the command string to # list then it should be the words of the command string to
@ -371,25 +644,16 @@ class CommandAction(_ActionAction):
# variables. # variables.
if __debug__: logInstanceCreation(self, 'Action.CommandAction') if __debug__: logInstanceCreation(self, 'Action.CommandAction')
if not cmdstr is None: #TODO(1.5) _ActionAction.__init__(self, **kw)
if callable(cmdstr): apply(_ActionAction.__init__, (self,), kw)
args = (cmdstr,)+args if is_List(cmd):
elif not SCons.Util.is_String(cmdstr): if filter(is_List, cmd):
raise SCons.Errors.UserError(\
'Invalid command display variable type. ' \
'You must either pass a string or a callback which ' \
'accepts (target, source, env) as parameters.')
apply(_ActionAction.__init__, (self,)+args, kw)
if SCons.Util.is_List(cmd):
if filter(SCons.Util.is_List, cmd):
raise TypeError, "CommandAction should be given only " \ raise TypeError, "CommandAction should be given only " \
"a single command" "a single command"
self.cmd_list = cmd self.cmd_list = cmd
self.cmdstr = cmdstr
def __str__(self): def __str__(self):
if SCons.Util.is_List(self.cmd_list): if is_List(self.cmd_list):
return string.join(map(str, self.cmd_list), ' ') return string.join(map(str, self.cmd_list), ' ')
return str(self.cmd_list) return str(self.cmd_list)
@ -412,7 +676,9 @@ class CommandAction(_ActionAction):
return result, ignore, silent return result, ignore, silent
def strfunction(self, target, source, env): def strfunction(self, target, source, env):
if not self.cmdstr is None: if self.cmdstr is None:
return None
if self.cmdstr is not _null:
from SCons.Subst import SUBST_RAW from SCons.Subst import SUBST_RAW
c = env.subst(self.cmdstr, SUBST_RAW, target, source) c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c: if c:
@ -431,11 +697,8 @@ class CommandAction(_ActionAction):
handle lists of commands, even though that's not how we use it handle lists of commands, even though that's not how we use it
externally. externally.
""" """
from SCons.Subst import escape_list escape_list = SCons.Subst.escape_list
import SCons.Util flatten_sequence = SCons.Util.flatten_sequence
flatten = SCons.Util.flatten
is_String = SCons.Util.is_String
is_List = SCons.Util.is_List
try: try:
shell = env['SHELL'] shell = env['SHELL']
@ -452,14 +715,7 @@ class CommandAction(_ActionAction):
escape = env.get('ESCAPE', lambda x: x) escape = env.get('ESCAPE', lambda x: x)
try: ENV = get_default_ENV(env)
ENV = env['ENV']
except KeyError:
global default_ENV
if not default_ENV:
import SCons.Environment
default_ENV = SCons.Environment.Environment()['ENV']
ENV = default_ENV
# Ensure that the ENV values are all strings: # Ensure that the ENV values are all strings:
for key, value in ENV.items(): for key, value in ENV.items():
@ -468,7 +724,7 @@ class CommandAction(_ActionAction):
# If the value is a list, then we assume it is a # If the value is a list, then we assume it is a
# path list, because that's a pretty common list-like # path list, because that's a pretty common list-like
# value to stick in an environment variable: # value to stick in an environment variable:
value = flatten(value) value = flatten_sequence(value)
ENV[key] = string.join(map(str, value), os.pathsep) ENV[key] = string.join(map(str, value), os.pathsep)
else: else:
# If it isn't a string or a list, then we just coerce # If it isn't a string or a list, then we just coerce
@ -492,7 +748,7 @@ class CommandAction(_ActionAction):
command=cmd_line) command=cmd_line)
return 0 return 0
def get_contents(self, target, source, env): def get_presig(self, target, source, env):
"""Return the signature contents of this action's command line. """Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string, This strips $(-$) and everything in between the string,
@ -500,7 +756,7 @@ class CommandAction(_ActionAction):
""" """
from SCons.Subst import SUBST_SIG from SCons.Subst import SUBST_SIG
cmd = self.cmd_list cmd = self.cmd_list
if SCons.Util.is_List(cmd): if is_List(cmd):
cmd = string.join(map(str, cmd)) cmd = string.join(map(str, cmd))
else: else:
cmd = str(cmd) cmd = str(cmd)
@ -508,7 +764,7 @@ class CommandAction(_ActionAction):
def get_implicit_deps(self, target, source, env): def get_implicit_deps(self, target, source, env):
icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
if SCons.Util.is_String(icd) and icd[:1] == '$': if is_String(icd) and icd[:1] == '$':
icd = env.subst(icd) icd = env.subst(icd)
if not icd or icd in ('0', 'None'): if not icd or icd in ('0', 'None'):
return [] return []
@ -524,20 +780,21 @@ class CommandAction(_ActionAction):
class CommandGeneratorAction(ActionBase): class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions.""" """Class for command-generator actions."""
def __init__(self, generator, *args, **kw): def __init__(self, generator, kw):
if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
self.generator = generator self.generator = generator
self.gen_args = args
self.gen_kw = kw self.gen_kw = kw
self.varlist = kw.get('varlist', ())
def _generate(self, target, source, env, for_signature): def _generate(self, target, source, env, for_signature):
# ensure that target is a list, to make it easier to write # ensure that target is a list, to make it easier to write
# generator functions: # generator functions:
if not SCons.Util.is_List(target): if not is_List(target):
target = [target] target = [target]
ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw) #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw)
gen_cmd = apply(Action, (ret,), self.gen_kw)
if not gen_cmd: if not gen_cmd:
raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
return gen_cmd return gen_cmd
@ -561,13 +818,13 @@ class CommandGeneratorAction(ActionBase):
return act(target, source, env, exitstatfunc, presub, return act(target, source, env, exitstatfunc, presub,
show, execute, chdir) show, execute, chdir)
def get_contents(self, target, source, env): def get_presig(self, target, source, env):
"""Return the signature contents of this action's command line. """Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string, This strips $(-$) and everything in between the string,
since those parts don't affect signatures. since those parts don't affect signatures.
""" """
return self._generate(target, source, env, 1).get_contents(target, source, env) return self._generate(target, source, env, 1).get_presig(target, source, env)
def get_implicit_deps(self, target, source, env): def get_implicit_deps(self, target, source, env):
return self._generate(target, source, env, 1).get_implicit_deps(target, source, env) return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
@ -593,22 +850,23 @@ class CommandGeneratorAction(ActionBase):
class LazyAction(CommandGeneratorAction, CommandAction): class LazyAction(CommandGeneratorAction, CommandAction):
def __init__(self, var, *args, **kw): def __init__(self, var, kw):
if __debug__: logInstanceCreation(self, 'Action.LazyAction') if __debug__: logInstanceCreation(self, 'Action.LazyAction')
apply(CommandAction.__init__, (self, '$'+var)+args, kw) #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
apply(CommandAction.__init__, (self, '${'+var+'}'), kw)
self.var = SCons.Util.to_String(var) self.var = SCons.Util.to_String(var)
self.gen_args = args
self.gen_kw = kw self.gen_kw = kw
def get_parent_class(self, env): def get_parent_class(self, env):
c = env.get(self.var) c = env.get(self.var)
if SCons.Util.is_String(c) and not '\n' in c: if is_String(c) and not '\n' in c:
return CommandAction return CommandAction
return CommandGeneratorAction return CommandGeneratorAction
def _generate_cache(self, env): def _generate_cache(self, env):
c = env.get(self.var, '') c = env.get(self.var, '')
gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw) #TODO(1.5) gen_cmd = Action(c, **self.gen_kw)
gen_cmd = apply(Action, (c,), self.gen_kw)
if not gen_cmd: if not gen_cmd:
raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
return gen_cmd return gen_cmd
@ -619,33 +877,34 @@ class LazyAction(CommandGeneratorAction, CommandAction):
def __call__(self, target, source, env, *args, **kw): def __call__(self, target, source, env, *args, **kw):
args = (self, target, source, env) + args args = (self, target, source, env) + args
c = self.get_parent_class(env) c = self.get_parent_class(env)
#TODO(1.5) return c.__call__(*args, **kw)
return apply(c.__call__, args, kw) return apply(c.__call__, args, kw)
def get_contents(self, target, source, env): def get_presig(self, target, source, env):
c = self.get_parent_class(env) c = self.get_parent_class(env)
return c.get_contents(self, target, source, env) return c.get_presig(self, target, source, env)
class FunctionAction(_ActionAction): class FunctionAction(_ActionAction):
"""Class for Python function actions.""" """Class for Python function actions."""
def __init__(self, execfunction, cmdstr=_null, *args, **kw): def __init__(self, execfunction, kw):
if __debug__: logInstanceCreation(self, 'Action.FunctionAction') if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
if not cmdstr is _null:
if callable(cmdstr):
args = (cmdstr,)+args
elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
raise SCons.Errors.UserError(\
'Invalid function display variable type. ' \
'You must either pass a string or a callback which ' \
'accepts (target, source, env) as parameters.')
self.execfunction = execfunction self.execfunction = execfunction
apply(_ActionAction.__init__, (self,)+args, kw) try:
self.varlist = kw.get('varlist', []) self.funccontents = _callable_contents(execfunction)
self.cmdstr = cmdstr except AttributeError:
try:
# See if execfunction will do the heavy lifting for us.
self.gc = execfunction.get_contents
except AttributeError:
# This is weird, just do the best we can.
self.funccontents = _object_contents(execfunction)
#TODO(1.5) _ActionAction.__init__(self, **kw)
apply(_ActionAction.__init__, (self,), kw)
def function_name(self): def function_name(self):
try: try:
@ -659,14 +918,20 @@ class FunctionAction(_ActionAction):
def strfunction(self, target, source, env): def strfunction(self, target, source, env):
if self.cmdstr is None: if self.cmdstr is None:
return None return None
if not self.cmdstr is _null: if self.cmdstr is not _null:
from SCons.Subst import SUBST_RAW from SCons.Subst import SUBST_RAW
c = env.subst(self.cmdstr, SUBST_RAW, target, source) c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c: if c:
return c return c
def array(a): def array(a):
def quote(s): def quote(s):
return '"' + str(s) + '"' try:
str_for_display = s.str_for_display
except AttributeError:
s = repr(s)
else:
s = str_for_display()
return s
return '[' + string.join(map(quote, a), ", ") + ']' return '[' + string.join(map(quote, a), ", ") + ']'
try: try:
strfunc = self.execfunction.strfunction strfunc = self.execfunction.strfunction
@ -689,73 +954,49 @@ class FunctionAction(_ActionAction):
return "%s(target, source, env)" % name return "%s(target, source, env)" % name
def execute(self, target, source, env): def execute(self, target, source, env):
exc_info = (None,None,None)
try:
rsources = map(rfile, source) rsources = map(rfile, source)
try: try:
result = self.execfunction(target=target, source=rsources, env=env) result = self.execfunction(target=target, source=rsources, env=env)
except EnvironmentError, e: except KeyboardInterrupt, e:
# If an IOError/OSError happens, raise a BuildError. raise
# Report the name of the file or directory that caused the except SystemExit, e:
# error, which might be different from the target being built raise
# (for example, failure to create the directory in which the except Exception, e:
# target file will appear). result = e
try: filename = e.filename exc_info = sys.exc_info()
except AttributeError: filename = None
result = SCons.Errors.BuildError(node=target,
errstr=e.strerror,
status=1,
filename=filename,
action=self,
command=self.strfunction(target, source, env))
else:
if result: if result:
msg = "Error %s" % result result = SCons.Errors.convert_to_BuildError(result, exc_info)
result = SCons.Errors.BuildError(errstr=msg, result.node=target
status=result, result.action=self
action=self, result.command=self.strfunction(target, source, env)
command=self.strfunction(target, source, env))
# FIXME: This maintains backward compatibility with respect to
# which type of exceptions were returned by raising an
# exception and which ones were returned by value. It would
# probably be best to always return them by value here, but
# some codes do not check the return value of Actions and I do
# not have the time to modify them at this point.
if (exc_info[1] and
not isinstance(exc_info[1],EnvironmentError)):
raise result
return result return result
finally:
# Break the cycle between the traceback object and this
# function stack frame. See the sys.exc_info() doc info for
# more information about this issue.
del exc_info
def get_contents(self, target, source, env):
"""Return the signature contents of this callable action.
By providing direct access to the code object of the def get_presig(self, target, source, env):
function, Python makes this extremely easy. Hooray! """Return the signature contents of this callable action."""
Unfortunately, older versions of Python include line
number indications in the compiled byte code. Boo!
So we remove the line number byte codes to prevent
recompilations from moving a Python function.
"""
execfunction = self.execfunction
try: try:
# Test if execfunction is a function. return self.gc(target, source, env)
code = execfunction.func_code.co_code
except AttributeError: except AttributeError:
try: return self.funccontents
# Test if execfunction is a method.
code = execfunction.im_func.func_code.co_code
except AttributeError:
try:
# Test if execfunction is a callable object.
code = execfunction.__call__.im_func.func_code.co_code
except AttributeError:
try:
# See if execfunction will do the heavy lifting for us.
gc = self.execfunction.get_contents
except AttributeError:
# This is weird, just do the best we can.
contents = str(self.execfunction)
else:
contents = gc(target, source, env)
else:
contents = str(code)
else:
contents = str(code)
else:
contents = str(code)
contents = remove_set_lineno_codes(contents)
return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
self.varlist)))
def get_implicit_deps(self, target, source, env): def get_implicit_deps(self, target, source, env):
return [] return []
@ -769,6 +1010,9 @@ class ListAction(ActionBase):
return x return x
return Action(x) return Action(x)
self.list = map(list_of_actions, list) self.list = map(list_of_actions, list)
# our children will have had any varlist
# applied; we don't need to do it again
self.varlist = ()
def genstring(self, target, source, env): def genstring(self, target, source, env):
return string.join(map(lambda a, t=target, s=source, e=env: return string.join(map(lambda a, t=target, s=source, e=env:
@ -780,11 +1024,10 @@ class ListAction(ActionBase):
return string.join(map(str, self.list), '\n') return string.join(map(str, self.list), '\n')
def presub_lines(self, env): def presub_lines(self, env):
return SCons.Util.flatten(map(lambda a, env=env: return SCons.Util.flatten_sequence(
a.presub_lines(env), map(lambda a, env=env: a.presub_lines(env), self.list))
self.list))
def get_contents(self, target, source, env): def get_presig(self, target, source, env):
"""Return the signature contents of this action list. """Return the signature contents of this action list.
Simple concatenation of the signatures of the elements. Simple concatenation of the signatures of the elements.
@ -822,6 +1065,7 @@ class ActionCaller:
self.parent = parent self.parent = parent
self.args = args self.args = args
self.kw = kw self.kw = kw
def get_contents(self, target, source, env): def get_contents(self, target, source, env):
actfunc = self.parent.actfunc actfunc = self.parent.actfunc
try: try:
@ -837,33 +1081,50 @@ class ActionCaller:
contents = str(actfunc) contents = str(actfunc)
contents = remove_set_lineno_codes(contents) contents = remove_set_lineno_codes(contents)
return contents return contents
def subst(self, s, target, source, env): def subst(self, s, target, source, env):
# If s is a list, recursively apply subst()
# to every element in the list
if is_List(s):
result = []
for elem in s:
result.append(self.subst(elem, target, source, env))
return self.parent.convert(result)
# Special-case hack: Let a custom function wrapped in an # Special-case hack: Let a custom function wrapped in an
# ActionCaller get at the environment through which the action # ActionCaller get at the environment through which the action
# was called by using this hard-coded value as a special return. # was called by using this hard-coded value as a special return.
if s == '$__env__': if s == '$__env__':
return env return env
elif SCons.Util.is_String(s): elif is_String(s):
return env.subst(s, 0, target, source) return env.subst(s, 1, target, source)
return self.parent.convert(s) return self.parent.convert(s)
def subst_args(self, target, source, env): def subst_args(self, target, source, env):
return map(lambda x, self=self, t=target, s=source, e=env: return map(lambda x, self=self, t=target, s=source, e=env:
self.subst(x, t, s, e), self.subst(x, t, s, e),
self.args) self.args)
def subst_kw(self, target, source, env): def subst_kw(self, target, source, env):
kw = {} kw = {}
for key in self.kw.keys(): for key in self.kw.keys():
kw[key] = self.subst(self.kw[key], target, source, env) kw[key] = self.subst(self.kw[key], target, source, env)
return kw return kw
def __call__(self, target, source, env): def __call__(self, target, source, env):
args = self.subst_args(target, source, env) args = self.subst_args(target, source, env)
kw = self.subst_kw(target, source, env) kw = self.subst_kw(target, source, env)
#TODO(1.5) return self.parent.actfunc(*args, **kw)
return apply(self.parent.actfunc, args, kw) return apply(self.parent.actfunc, args, kw)
def strfunction(self, target, source, env): def strfunction(self, target, source, env):
args = self.subst_args(target, source, env) args = self.subst_args(target, source, env)
kw = self.subst_kw(target, source, env) kw = self.subst_kw(target, source, env)
#TODO(1.5) return self.parent.strfunc(*args, **kw)
return apply(self.parent.strfunc, args, kw) return apply(self.parent.strfunc, args, kw)
def __str__(self): def __str__(self):
#TODO(1.5) return self.parent.strfunc(*self.args, **self.kw)
return apply(self.parent.strfunc, self.args, self.kw) return apply(self.parent.strfunc, self.args, self.kw)
class ActionFactory: class ActionFactory:
@ -879,6 +1140,7 @@ class ActionFactory:
self.actfunc = actfunc self.actfunc = actfunc
self.strfunc = strfunc self.strfunc = strfunc
self.convert = convert self.convert = convert
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
ac = ActionCaller(self, args, kw) ac = ActionCaller(self, args, kw)
action = Action(ac, strfunction=ac.strfunction) action = Action(ac, strfunction=ac.strfunction)

View file

@ -76,7 +76,7 @@ There are the following methods for internal use within this module:
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -98,9 +98,7 @@ There are the following methods for internal use within this module:
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Builder.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Builder.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import UserDict import UserDict
import UserList import UserList
@ -234,7 +232,7 @@ def Builder(**kw):
if kw.has_key('generator'): if kw.has_key('generator'):
if kw.has_key('action'): if kw.has_key('action'):
raise UserError, "You must not specify both an action and a generator." raise UserError, "You must not specify both an action and a generator."
kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator']) kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
del kw['generator'] del kw['generator']
elif kw.has_key('action'): elif kw.has_key('action'):
source_ext_match = kw.get('source_ext_match', 1) source_ext_match = kw.get('source_ext_match', 1)
@ -242,7 +240,7 @@ def Builder(**kw):
del kw['source_ext_match'] del kw['source_ext_match']
if SCons.Util.is_Dict(kw['action']): if SCons.Util.is_Dict(kw['action']):
composite = DictCmdGenerator(kw['action'], source_ext_match) composite = DictCmdGenerator(kw['action'], source_ext_match)
kw['action'] = SCons.Action.CommandGeneratorAction(composite) kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
kw['src_suffix'] = composite.src_suffixes() kw['src_suffix'] = composite.src_suffixes()
else: else:
kw['action'] = SCons.Action.Action(kw['action']) kw['action'] = SCons.Action.Action(kw['action'])
@ -363,7 +361,7 @@ class BuilderBase:
name = None, name = None,
chdir = _null, chdir = _null,
is_explicit = 1, is_explicit = 1,
src_builder = [], src_builder = None,
ensure_suffix = False, ensure_suffix = False,
**overrides): **overrides):
if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
@ -410,7 +408,9 @@ class BuilderBase:
self.executor_kw['chdir'] = chdir self.executor_kw['chdir'] = chdir
self.is_explicit = is_explicit self.is_explicit = is_explicit
if not SCons.Util.is_List(src_builder): if src_builder is None:
src_builder = []
elif not SCons.Util.is_List(src_builder):
src_builder = [ src_builder ] src_builder = [ src_builder ]
self.src_builder = src_builder self.src_builder = src_builder
@ -505,7 +505,7 @@ class BuilderBase:
tlist = [ t_from_s(pre, suf, splitext) ] tlist = [ t_from_s(pre, suf, splitext) ]
else: else:
target = self._adjustixes(target, pre, suf, self.ensure_suffix) target = self._adjustixes(target, pre, suf, self.ensure_suffix)
tlist = env.arg2nodes(target, target_factory) tlist = env.arg2nodes(target, target_factory, target=target, source=source)
if self.emitter: if self.emitter:
# The emitter is going to do str(node), but because we're # The emitter is going to do str(node), but because we're
@ -712,14 +712,9 @@ class BuilderBase:
return None return None
result = [] result = []
for s in SCons.Util.flatten(source):
if SCons.Util.is_List(source):
source = SCons.Util.flatten(source)
else:
source = [source]
for s in source:
if SCons.Util.is_String(s): if SCons.Util.is_String(s):
match_suffix = match_src_suffix(s) match_suffix = match_src_suffix(env.subst(s))
if not match_suffix and not '.' in s: if not match_suffix and not '.' in s:
src_suf = self.get_src_suffix(env) src_suf = self.get_src_suffix(env)
s = self._adjustixes(s, None, src_suf)[0] s = self._adjustixes(s, None, src_suf)[0]

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/CacheDir.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/CacheDir.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """ __doc__ = """
CacheDir support CacheDir support
@ -34,6 +34,7 @@ import sys
import SCons.Action import SCons.Action
cache_enabled = True
cache_debug = False cache_debug = False
cache_force = False cache_force = False
cache_show = False cache_show = False
@ -129,31 +130,33 @@ class CacheDir:
except ImportError: except ImportError:
msg = "No hashlib or MD5 module available, CacheDir() not supported" msg = "No hashlib or MD5 module available, CacheDir() not supported"
SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
self.path = None
else: else:
self.path = path self.path = path
self.current_cache_debug = None
self.debugFP = None
def CacheDebugWrite(self, fmt, target, cachefile): def CacheDebug(self, fmt, target, cachefile):
self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) if cache_debug != self.current_cache_debug:
def CacheDebugQuiet(self, fmt, target, cachefile):
pass
def CacheDebugInit(self, fmt, target, cachefile):
if cache_debug:
if cache_debug == '-': if cache_debug == '-':
self.debugFP = sys.stdout self.debugFP = sys.stdout
else: elif cache_debug:
self.debugFP = open(cache_debug, 'w') self.debugFP = open(cache_debug, 'w')
self.CacheDebug = self.CacheDebugWrite
self.CacheDebug(fmt, target, cachefile)
else: else:
self.CacheDebug = self.CacheDebugQuiet self.debugFP = None
self.current_cache_debug = cache_debug
if self.debugFP:
self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
CacheDebug = CacheDebugInit def is_enabled(self):
return (cache_enabled and not self.path is None)
def cachepath(self, node): def cachepath(self, node):
""" """
""" """
if not self.is_enabled():
return None, None
sig = node.get_cachedir_bsig() sig = node.get_cachedir_bsig()
subdir = string.upper(sig[0]) subdir = string.upper(sig[0])
dir = os.path.join(self.path, subdir) dir = os.path.join(self.path, subdir)
@ -184,6 +187,9 @@ class CacheDir:
execute the CacheRetrieveFunc and then have the latter execute the CacheRetrieveFunc and then have the latter
explicitly check SCons.Action.execute_actions itself. explicitly check SCons.Action.execute_actions itself.
""" """
if not self.is_enabled():
return False
retrieved = False retrieved = False
if cache_show: if cache_show:
@ -202,16 +208,10 @@ class CacheDir:
return retrieved return retrieved
def push(self, node): def push(self, node):
if not self.is_enabled():
return
return CachePush(node, [], node.get_build_env()) return CachePush(node, [], node.get_build_env())
def push_if_forced(self, node): def push_if_forced(self, node):
if cache_force: if cache_force:
return self.push(node) return self.push(node)
class Null(SCons.Util.Null):
def repr(self):
return 'CacheDir.Null()'
def cachepath(self, node):
return None, None
def retrieve(self, node):
return False

View file

@ -142,6 +142,101 @@ int main() {
_YesNoResult(context, ret, None, text) _YesNoResult(context, ret, None, text)
return ret return ret
def CheckCC(context):
"""
Configure check for a working C compiler.
This checks whether the C compiler, as defined in the $CC construction
variable, can compile a C source file. It uses the current $CCCOM value
too, so that it can test against non working flags.
"""
context.Display("Checking whether the C compiler works")
text = """
int main()
{
return 0;
}
"""
ret = _check_empty_program(context, 'CC', text, 'C')
_YesNoResult(context, ret, None, text)
return ret
def CheckSHCC(context):
"""
Configure check for a working shared C compiler.
This checks whether the C compiler, as defined in the $SHCC construction
variable, can compile a C source file. It uses the current $SHCCCOM value
too, so that it can test against non working flags.
"""
context.Display("Checking whether the (shared) C compiler works")
text = """
int foo()
{
return 0;
}
"""
ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True)
_YesNoResult(context, ret, None, text)
return ret
def CheckCXX(context):
"""
Configure check for a working CXX compiler.
This checks whether the CXX compiler, as defined in the $CXX construction
variable, can compile a CXX source file. It uses the current $CXXCOM value
too, so that it can test against non working flags.
"""
context.Display("Checking whether the C++ compiler works")
text = """
int main()
{
return 0;
}
"""
ret = _check_empty_program(context, 'CXX', text, 'C++')
_YesNoResult(context, ret, None, text)
return ret
def CheckSHCXX(context):
"""
Configure check for a working shared CXX compiler.
This checks whether the CXX compiler, as defined in the $SHCXX construction
variable, can compile a CXX source file. It uses the current $SHCXXCOM value
too, so that it can test against non working flags.
"""
context.Display("Checking whether the (shared) C++ compiler works")
text = """
int main()
{
return 0;
}
"""
ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True)
_YesNoResult(context, ret, None, text)
return ret
def _check_empty_program(context, comp, text, language, use_shared = False):
"""Return 0 on success, 1 otherwise."""
if not context.env.has_key(comp) or not context.env[comp]:
# The compiler construction variable is not set or empty
return 1
lang, suffix, msg = _lang2suffix(language)
if msg:
return 1
if use_shared:
return context.CompileSharedObject(text, suffix)
else:
return context.CompileProg(text, suffix)
def CheckFunc(context, function_name, header = None, language = None): def CheckFunc(context, function_name, header = None, language = None):
""" """
@ -206,7 +301,9 @@ int main() {
context.Display("Checking for %s function %s()... " % (lang, function_name)) context.Display("Checking for %s function %s()... " % (lang, function_name))
ret = context.BuildProg(text, suffix) ret = context.BuildProg(text, suffix)
_YesNoResult(context, ret, "HAVE_" + function_name, text) _YesNoResult(context, ret, "HAVE_" + function_name, text,
"Define to 1 if the system has the function `%s'." %\
function_name)
return ret return ret
@ -253,7 +350,8 @@ def CheckHeader(context, header_name, header = None, language = None,
context.Display("Checking for %s header file %s... " % (lang, header_name)) context.Display("Checking for %s header file %s... " % (lang, header_name))
ret = context.CompileProg(text, suffix) ret = context.CompileProg(text, suffix)
_YesNoResult(context, ret, "HAVE_" + header_name, text) _YesNoResult(context, ret, "HAVE_" + header_name, text,
"Define to 1 if you have the <%s> header file." % header_name)
return ret return ret
@ -310,7 +408,8 @@ int main() {
context.Display("Checking for %s type %s... " % (lang, type_name)) context.Display("Checking for %s type %s... " % (lang, type_name))
ret = context.BuildProg(text, suffix) ret = context.BuildProg(text, suffix)
_YesNoResult(context, ret, "HAVE_" + type_name, text) _YesNoResult(context, ret, "HAVE_" + type_name, text,
"Define to 1 if the system has the type `%s'." % type_name)
if ret and fallback and context.headerfilename: if ret and fallback and context.headerfilename:
f = open(context.headerfilename, "a") f = open(context.headerfilename, "a")
f.write("typedef %s %s;\n" % (fallback, type_name)) f.write("typedef %s %s;\n" % (fallback, type_name))
@ -371,11 +470,11 @@ int main()
} }
""" """
# XXX: Try* vs CompileProg ? st = context.CompileProg(src % (type_name, expect), suffix)
st = context.TryCompile(src % (type_name, expect), suffix) if not st:
if st:
_Have(context, "SIZEOF_" + type_name, str(expect))
context.Display("yes\n") context.Display("yes\n")
_Have(context, "SIZEOF_%s" % type_name, expect,
"The size of `%s', as computed by sizeof." % type_name)
return expect return expect
else: else:
context.Display("no\n") context.Display("no\n")
@ -400,21 +499,78 @@ int main() {
return 0; return 0;
} }
""" """
ret = context.TryRun(src, suffix) st, out = context.RunProg(src, suffix)
st = ret[0]
try: try:
size = int(ret[1]) size = int(out)
_Have(context, "SIZEOF_" + type_name, str(size))
context.Display("%d\n" % size)
except ValueError: except ValueError:
# If cannot convert output of test prog to an integer (the size),
# something went wront, so just fail
st = 1
size = 0 size = 0
_LogFailed(context, src, st)
context.Display(" Failed !\n") if not st:
if st: context.Display("yes\n")
_Have(context, "SIZEOF_%s" % type_name, size,
"The size of `%s', as computed by sizeof." % type_name)
return size return size
else: else:
context.Display("no\n")
_LogFailed(context, src, st)
return 0 return 0
return 0
def CheckDeclaration(context, symbol, includes = None, language = None):
"""Checks whether symbol is declared.
Use the same test as autoconf, that is test whether the symbol is defined
as a macro or can be used as an r-value.
Arguments:
symbol : str
the symbol to check
includes : str
Optional "header" can be defined to include a header file.
language : str
only C and C++ supported.
Returns:
status : bool
True if the check failed, False if succeeded."""
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
if context.headerfilename:
includetext = '#include "%s"' % context.headerfilename
else:
includetext = ''
if not includes:
includes = ""
lang, suffix, msg = _lang2suffix(language)
if msg:
context.Display("Cannot check for declaration %s: %s\n" % (type_name, msg))
return msg
src = includetext + includes
context.Display('Checking whether %s is declared... ' % symbol)
src = src + r"""
int main()
{
#ifndef %s
(void) %s;
#endif
;
return 0;
}
""" % (symbol, symbol)
st = context.CompileProg(src, suffix)
_YesNoResult(context, st, "HAVE_DECL_" + symbol, src,
"Set to 1 if %s is defined." % symbol)
return st
def CheckLib(context, libs, func_name = None, header = None, def CheckLib(context, libs, func_name = None, header = None,
extra_libs = None, call = None, language = None, autoadd = 1): extra_libs = None, call = None, language = None, autoadd = 1):
""" """
@ -509,7 +665,8 @@ return 0;
ret = context.BuildProg(text, suffix) ret = context.BuildProg(text, suffix)
_YesNoResult(context, ret, sym, text) _YesNoResult(context, ret, sym, text,
"Define to 1 if you have the `%s' library." % lib_name)
if oldLIBS != -1 and (ret or not autoadd): if oldLIBS != -1 and (ret or not autoadd):
context.SetLIBS(oldLIBS) context.SetLIBS(oldLIBS)
@ -522,15 +679,17 @@ return 0;
# END OF PUBLIC FUNCTIONS # END OF PUBLIC FUNCTIONS
# #
def _YesNoResult(context, ret, key, text): def _YesNoResult(context, ret, key, text, comment = None):
""" """
Handle the result of a test with a "yes" or "no" result. Handle the result of a test with a "yes" or "no" result.
"ret" is the return value: empty if OK, error message when not. "ret" is the return value: empty if OK, error message when not.
"key" is the name of the symbol to be defined (HAVE_foo). "key" is the name of the symbol to be defined (HAVE_foo).
"text" is the source code of the program used for testing. "text" is the source code of the program used for testing.
"comment" is the C comment to add above the line defining the symbol (the
comment is automatically put inside a /* */). If None, no comment is added.
""" """
if key: if key:
_Have(context, key, not ret) _Have(context, key, not ret, comment)
if ret: if ret:
context.Display("no\n") context.Display("no\n")
_LogFailed(context, text, ret) _LogFailed(context, text, ret)
@ -538,7 +697,7 @@ def _YesNoResult(context, ret, key, text):
context.Display("yes\n") context.Display("yes\n")
def _Have(context, key, have): def _Have(context, key, have, comment = None):
""" """
Store result of a test in context.havedict and context.headerfilename. Store result of a test in context.havedict and context.headerfilename.
"key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non-
@ -566,12 +725,17 @@ def _Have(context, key, have):
else: else:
line = "#define %s %s\n" % (key_up, str(have)) line = "#define %s %s\n" % (key_up, str(have))
if comment is not None:
lines = "\n/* %s */\n" % comment + line
else:
lines = "\n" + line
if context.headerfilename: if context.headerfilename:
f = open(context.headerfilename, "a") f = open(context.headerfilename, "a")
f.write(line) f.write(lines)
f.close() f.close()
elif hasattr(context,'config_h'): elif hasattr(context,'config_h'):
context.config_h = context.config_h + line context.config_h = context.config_h + lines
def _LogFailed(context, text, msg): def _LogFailed(context, text, msg):

View file

@ -7,7 +7,7 @@ needed by most users.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -29,7 +29,7 @@ needed by most users.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Debug.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Debug.py 3842 2008/12/20 22:59:52 scons"
import os import os
import string import string
@ -115,39 +115,57 @@ else:
res = resource.getrusage(resource.RUSAGE_SELF) res = resource.getrusage(resource.RUSAGE_SELF)
return res[4] return res[4]
# returns caller's stack
def caller_stack(*backlist):
caller_dicts = {}
def caller(*backlist):
import traceback import traceback
if not backlist: if not backlist:
backlist = [0] backlist = [0]
result = [] result = []
for back in backlist: for back in backlist:
tb = traceback.extract_stack(limit=3+back) tb = traceback.extract_stack(limit=3+back)
key = tb[1][:3]
try:
entry = caller_dicts[key]
except KeyError:
entry = caller_dicts[key] = {}
key = tb[0][:3] key = tb[0][:3]
entry[key] = entry.get(key, 0) + 1
result.append('%s:%d(%s)' % func_shorten(key)) result.append('%s:%d(%s)' % func_shorten(key))
return result return result
caller_bases = {}
caller_dicts = {}
# trace a caller's stack
def caller_trace(back=0):
import traceback
tb = traceback.extract_stack(limit=3+back)
tb.reverse()
callee = tb[1][:3]
caller_bases[callee] = caller_bases.get(callee, 0) + 1
for caller in tb[2:]:
caller = callee + caller[:3]
try:
entry = caller_dicts[callee]
except KeyError:
caller_dicts[callee] = entry = {}
entry[caller] = entry.get(caller, 0) + 1
callee = caller
# print a single caller and its callers, if any
def _dump_one_caller(key, file, level=0):
l = []
for c,v in caller_dicts[key].items():
l.append((-v,c))
l.sort()
leader = ' '*level
for v,c in l:
file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:])))
if caller_dicts.has_key(c):
_dump_one_caller(c, file, level+1)
# print each call tree
def dump_caller_counts(file=sys.stdout): def dump_caller_counts(file=sys.stdout):
keys = caller_dicts.keys() keys = caller_bases.keys()
keys.sort() keys.sort()
for k in keys: for k in keys:
file.write("Callers of %s:%d(%s):\n" % func_shorten(k)) file.write("Callers of %s:%d(%s), %d calls:\n"
counts = caller_dicts[k] % (func_shorten(k) + (caller_bases[k],)))
callers = counts.keys() _dump_one_caller(k, file)
callers.sort()
for c in callers:
#file.write(" counts[%s] = %s\n" % (c, counts[c]))
t = ((counts[c],) + func_shorten(c))
file.write(" %6d %s:%d(%s)\n" % t)
shorten_list = [ shorten_list = [
( '/scons/SCons/', 1), ( '/scons/SCons/', 1),
@ -168,10 +186,8 @@ def func_shorten(func_tuple):
if i >= 0: if i >= 0:
if t[1]: if t[1]:
i = i + len(t[0]) i = i + len(t[0])
f = f[i:] return (f[i:],)+func_tuple[1:]
break return func_tuple
return (f,)+func_tuple[1:]
TraceFP = {} TraceFP = {}

View file

@ -10,7 +10,7 @@ from distutils.msvccompiler.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -32,7 +32,7 @@ from distutils.msvccompiler.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Defaults.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Defaults.py 3842 2008/12/20 22:59:52 scons"
@ -40,6 +40,7 @@ import os
import os.path import os.path
import shutil import shutil
import stat import stat
import string
import time import time
import types import types
import sys import sys
@ -93,7 +94,7 @@ def DefaultEnvironment(*args, **kw):
_default_env.Decider('timestamp-match') _default_env.Decider('timestamp-match')
global DefaultEnvironment global DefaultEnvironment
DefaultEnvironment = _fetch_DefaultEnvironment DefaultEnvironment = _fetch_DefaultEnvironment
_default_env._CacheDir = SCons.CacheDir.Null() _default_env._CacheDir_path = None
return _default_env return _default_env
# Emitters for setting the shared attribute on object files, # Emitters for setting the shared attribute on object files,
@ -157,19 +158,36 @@ LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
# ways by creating ActionFactory instances. # ways by creating ActionFactory instances.
ActionFactory = SCons.Action.ActionFactory ActionFactory = SCons.Action.ActionFactory
def chmod_func(path, mode): def get_paths_str(dest):
return os.chmod(str(path), mode) # 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 '[' + string.join(elem_strs, ', ') + ']'
else:
return '"' + str(dest) + '"'
Chmod = ActionFactory(chmod_func, def chmod_func(dest, mode):
lambda dest, mode: 'Chmod("%s", 0%o)' % (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 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): def copy_func(dest, src):
SCons.Node.FS.invalidate_node_memos(dest)
if SCons.Util.is_List(src) and os.path.isdir(dest): if SCons.Util.is_List(src) and os.path.isdir(dest):
for file in src: for file in src:
shutil.copy(file, dest) shutil.copy2(file, dest)
return 0 return 0
elif os.path.isfile(src): elif os.path.isfile(src):
return shutil.copy(src, dest) return shutil.copy2(src, dest)
else: else:
return shutil.copytree(src, dest, 1) return shutil.copytree(src, dest, 1)
@ -177,29 +195,50 @@ Copy = ActionFactory(copy_func,
lambda dest, src: 'Copy("%s", "%s")' % (dest, src), lambda dest, src: 'Copy("%s", "%s")' % (dest, src),
convert=str) convert=str)
def delete_func(entry, must_exist=0): 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) entry = str(entry)
if not must_exist and not os.path.exists(entry): if not must_exist and not os.path.exists(entry):
return None continue
if not os.path.exists(entry) or os.path.isfile(entry): if not os.path.exists(entry) or os.path.isfile(entry):
return os.unlink(entry) os.unlink(entry)
continue
else: else:
return shutil.rmtree(entry, 1) shutil.rmtree(entry, 1)
continue
def delete_strfunc(entry, must_exist=0): def delete_strfunc(dest, must_exist=0):
return 'Delete("%s")' % entry return 'Delete(%s)' % get_paths_str(dest)
Delete = ActionFactory(delete_func, delete_strfunc) Delete = ActionFactory(delete_func, delete_strfunc)
Mkdir = ActionFactory(os.makedirs, def mkdir_func(dest):
lambda dir: 'Mkdir("%s")' % dir, SCons.Node.FS.invalidate_node_memos(dest)
convert=str) if not SCons.Util.is_List(dest):
dest = [dest]
for entry in dest:
os.makedirs(str(entry))
Move = ActionFactory(lambda dest, src: os.rename(src, dest), 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)
os.rename(src, dest)
Move = ActionFactory(move_func,
lambda dest, src: 'Move("%s", "%s")' % (dest, src), lambda dest, src: 'Move("%s", "%s")' % (dest, src),
convert=str) convert=str)
def touch_func(file): 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) file = str(file)
mtime = int(time.time()) mtime = int(time.time())
if os.path.exists(file): if os.path.exists(file):
@ -207,10 +246,10 @@ def touch_func(file):
else: else:
open(file, 'w') open(file, 'w')
atime = mtime atime = mtime
return os.utime(file, (atime, mtime)) os.utime(file, (atime, mtime))
Touch = ActionFactory(touch_func, Touch = ActionFactory(touch_func,
lambda file: 'Touch("%s")' % file) lambda file: 'Touch(%s)' % get_paths_str(file))
# Internal utility functions # Internal utility functions
@ -224,9 +263,6 @@ def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
if not list: if not list:
return list return list
if SCons.Util.is_List(list):
list = SCons.Util.flatten(list)
l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
if not l is None: if not l is None:
list = l list = l
@ -270,7 +306,7 @@ def _concat_ixes(prefix, list, suffix, env):
return result return result
def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None): def _stripixes(prefix, list, suffix, stripprefixes, stripsuffixes, env, c=None):
""" """
This is a wrapper around _concat()/_concat_ixes() that checks for the This is a wrapper around _concat()/_concat_ixes() that checks for the
existence of prefixes or suffixes on list elements and strips them existence of prefixes or suffixes on list elements and strips them
@ -292,22 +328,32 @@ def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
else: else:
c = _concat_ixes c = _concat_ixes
if SCons.Util.is_List(list): stripprefixes = map(env.subst, SCons.Util.flatten(stripprefixes))
list = SCons.Util.flatten(list) stripsuffixes = map(env.subst, SCons.Util.flatten(stripsuffixes))
lsp = len(stripprefix)
lss = len(stripsuffix)
stripped = [] stripped = []
for l in SCons.PathList.PathList(list).subst_path(env, None, None): for l in SCons.PathList.PathList(list).subst_path(env, None, None):
if isinstance(l, SCons.Node.FS.File): if isinstance(l, SCons.Node.FS.File):
stripped.append(l) stripped.append(l)
continue continue
if not SCons.Util.is_String(l): if not SCons.Util.is_String(l):
l = str(l) l = str(l)
for stripprefix in stripprefixes:
lsp = len(stripprefix)
if l[:lsp] == stripprefix: if l[:lsp] == stripprefix:
l = l[lsp:] l = l[lsp:]
# Do not strip more than one prefix
break
for stripsuffix in stripsuffixes:
lss = len(stripsuffix)
if l[-lss:] == stripsuffix: if l[-lss:] == stripsuffix:
l = l[:-lss] l = l[:-lss]
# Do not strip more than one suffix
break
stripped.append(l) stripped.append(l)
return c(prefix, stripped, suffix, env) return c(prefix, stripped, suffix, env)
@ -378,7 +424,10 @@ class Variable_Method_Caller:
self.method = method self.method = method
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
try: 1/0 try: 1/0
except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame 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 variable = self.variable
while frame: while frame:
if frame.f_locals.has_key(variable): if frame.f_locals.has_key(variable):

View file

@ -10,7 +10,7 @@ Environment
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -32,12 +32,13 @@ Environment
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Environment.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Environment.py 3842 2008/12/20 22:59:52 scons"
import copy import copy
import os import os
import os.path import sys
import re
import shlex import shlex
import string import string
from UserDict import UserDict from UserDict import UserDict
@ -64,6 +65,10 @@ class _Null:
_null = _Null _null = _Null
_warn_copy_deprecated = True
_warn_source_signatures_deprecated = True
_warn_target_signatures_deprecated = True
CleanTargets = {} CleanTargets = {}
CalculatorArgs = {} CalculatorArgs = {}
@ -100,24 +105,40 @@ def apply_tools(env, tools, toolpath):
else: else:
env.Tool(tool) env.Tool(tool)
# These names are controlled by SCons; users should never set or override # These names are (or will be) controlled by SCons; users should never
# them. This warning can optionally be turned off, but scons will still # set or override them. This warning can optionally be turned off,
# ignore the illegal variable names even if it's off. # but scons will still ignore the illegal variable names even if it's off.
reserved_construction_var_names = \ reserved_construction_var_names = [
['TARGET', 'TARGETS', 'SOURCE', 'SOURCES'] 'SOURCE',
'SOURCES',
'TARGET',
'TARGETS',
]
future_reserved_construction_var_names = [
'CHANGED_SOURCES',
'CHANGED_TARGETS',
'UNCHANGED_SOURCES',
'UNCHANGED_TARGETS',
]
def copy_non_reserved_keywords(dict): def copy_non_reserved_keywords(dict):
result = semi_deepcopy(dict) result = semi_deepcopy(dict)
for k in result.keys(): for k in result.keys():
if k in reserved_construction_var_names: if k in reserved_construction_var_names:
SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg = "Ignoring attempt to set reserved variable `$%s'"
"Ignoring attempt to set reserved variable `%s'" % k) SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
del result[k] del result[k]
return result return result
def _set_reserved(env, key, value): def _set_reserved(env, key, value):
msg = "Ignoring attempt to set reserved variable `%s'" % key msg = "Ignoring attempt to set reserved variable `$%s'"
SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg) SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
def _set_future_reserved(env, key, value):
env._dict[key] = value
msg = "`$%s' will be reserved in a future release and setting it will become ignored"
SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
def _set_BUILDERS(env, key, value): def _set_BUILDERS(env, key, value):
try: try:
@ -137,6 +158,24 @@ def _set_SCANNERS(env, key, value):
env._dict[key] = value env._dict[key] = value
env.scanner_map_delete() env.scanner_map_delete()
def _delete_duplicates(l, keep_last):
"""Delete duplicates from a sequence, keeping the first or last."""
seen={}
result=[]
if keep_last: # reverse in & out, then keep first
l.reverse()
for i in l:
try:
if not seen.has_key(i):
result.append(i)
seen[i]=1
except TypeError:
# probably unhashable. Just keep it.
result.append(i)
if keep_last:
result.reverse()
return result
# The following is partly based on code in a comment added by Peter # The following is partly based on code in a comment added by Peter
@ -225,7 +264,7 @@ class BuilderWrapper(MethodWrapper):
elif name == 'builder': elif name == 'builder':
return self.method return self.method
else: else:
return self.__dict__[name] raise AttributeError, name
def __setattr__(self, name, value): def __setattr__(self, name, value):
if name == 'env': if name == 'env':
@ -279,6 +318,18 @@ class BuilderDict(UserDict):
for i, v in dict.items(): for i, v in dict.items():
self.__setitem__(i, v) self.__setitem__(i, v)
_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
def is_valid_construction_var(varstr):
"""Return if the specified string is a legitimate construction
variable.
"""
return _is_valid_var.match(varstr)
class SubstitutionEnvironment: class SubstitutionEnvironment:
"""Base class for different flavors of construction environments. """Base class for different flavors of construction environments.
@ -329,9 +380,16 @@ class SubstitutionEnvironment:
self._special_set = {} self._special_set = {}
for key in reserved_construction_var_names: for key in reserved_construction_var_names:
self._special_set[key] = _set_reserved self._special_set[key] = _set_reserved
for key in future_reserved_construction_var_names:
self._special_set[key] = _set_future_reserved
self._special_set['BUILDERS'] = _set_BUILDERS self._special_set['BUILDERS'] = _set_BUILDERS
self._special_set['SCANNERS'] = _set_SCANNERS self._special_set['SCANNERS'] = _set_SCANNERS
# Freeze the keys of self._special_set in a list for use by
# methods that need to check. (Empirically, list scanning has
# gotten better than dict.has_key() in Python 2.5.)
self._special_set_keys = self._special_set.keys()
def __cmp__(self, other): def __cmp__(self, other):
return cmp(self._dict, other._dict) return cmp(self._dict, other._dict)
@ -346,11 +404,27 @@ class SubstitutionEnvironment:
return self._dict[key] return self._dict[key]
def __setitem__(self, key, value): def __setitem__(self, key, value):
special = self._special_set.get(key) # This is heavily used. This implementation is the best we have
if special: # according to the timings in bench/env.__setitem__.py.
special(self, key, value) #
# The "key in self._special_set_keys" test here seems to perform
# pretty well for the number of keys we have. A hard-coded
# list works a little better in Python 2.5, but that has the
# disadvantage of maybe getting out of sync if we ever add more
# variable names. Using self._special_set.has_key() works a
# little better in Python 2.4, but is worse then this test.
# So right now it seems like a good trade-off, but feel free to
# revisit this with bench/env.__setitem__.py as needed (and
# as newer versions of Python come out).
if key in self._special_set_keys:
self._special_set[key](self, key, value)
else: else:
if not SCons.Util.is_valid_construction_var(key): # If we already have the entry, then it's obviously a valid
# key and we don't need to check. If we do check, using a
# global, pre-compiled regular expression directly is more
# efficient than calling another function or a method.
if not self._dict.has_key(key) \
and not _is_valid_var.match(key):
raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
self._dict[key] = value self._dict[key] = value
@ -361,6 +435,9 @@ class SubstitutionEnvironment:
def has_key(self, key): def has_key(self, key):
return self._dict.has_key(key) return self._dict.has_key(key)
def __contains__(self, key):
return self._dict.__contains__(key)
def items(self): def items(self):
return self._dict.items() return self._dict.items()
@ -373,10 +450,7 @@ class SubstitutionEnvironment:
if not args: if not args:
return [] return []
if SCons.Util.is_List(args):
args = SCons.Util.flatten(args) args = SCons.Util.flatten(args)
else:
args = [args]
nodes = [] nodes = []
for v in args: for v in args:
@ -465,7 +539,7 @@ class SubstitutionEnvironment:
try: try:
get = obj.get get = obj.get
except AttributeError: except AttributeError:
pass obj = SCons.Util.to_String_for_subst(obj)
else: else:
obj = get() obj = get()
return obj return obj
@ -481,7 +555,7 @@ class SubstitutionEnvironment:
# We have an object plus a string, or multiple # We have an object plus a string, or multiple
# objects that we need to smush together. No choice # objects that we need to smush together. No choice
# but to make them into a string. # but to make them into a string.
p = string.join(map(SCons.Util.to_String, p), '') p = string.join(map(SCons.Util.to_String_for_subst, p), '')
else: else:
p = s(p) p = s(p)
r.append(p) r.append(p)
@ -491,24 +565,21 @@ class SubstitutionEnvironment:
def backtick(self, command): def backtick(self, command):
import subprocess import subprocess
if SCons.Util.is_List(command): # common arguments
p = subprocess.Popen(command, kw = { 'stdin' : 'devnull',
stdout=subprocess.PIPE, 'stdout' : subprocess.PIPE,
stderr=subprocess.PIPE, 'stderr' : subprocess.PIPE,
universal_newlines=True) 'universal_newlines' : True,
else: }
p = subprocess.Popen(command, # if the command is a list, assume it's been quoted
stdout=subprocess.PIPE, # othewise force a shell
stderr=subprocess.PIPE, if not SCons.Util.is_List(command): kw['shell'] = True
universal_newlines=True, # run constructed command
shell=True) #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
out = p.stdout.read() p = apply(SCons.Action._subproc, (self, command), kw)
p.stdout.close() out,err = p.communicate()
err = p.stderr.read()
p.stderr.close()
status = p.wait() status = p.wait()
if err: if err:
import sys
sys.stderr.write(err) sys.stderr.write(err)
if status: if status:
raise OSError("'%s' exited %d" % (command, status)) raise OSError("'%s' exited %d" % (command, status))
@ -543,16 +614,19 @@ class SubstitutionEnvironment:
environment, and doesn't even create a wrapper object if there environment, and doesn't even create a wrapper object if there
are no overrides. are no overrides.
""" """
if overrides: if not overrides: return self
o = copy_non_reserved_keywords(overrides) o = copy_non_reserved_keywords(overrides)
if not o: return self
overrides = {} overrides = {}
merges = None
for key, value in o.items(): for key, value in o.items():
overrides[key] = SCons.Subst.scons_subst_once(value, self, key) if key == 'parse_flags':
if overrides: merges = value
env = OverrideEnvironment(self, overrides)
return env
else: else:
return self overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
env = OverrideEnvironment(self, overrides)
if merges: env.MergeFlags(merges)
return env
def ParseFlags(self, *flags): def ParseFlags(self, *flags):
""" """
@ -709,13 +783,16 @@ class SubstitutionEnvironment:
do_parse(arg, do_parse) do_parse(arg, do_parse)
return dict return dict
def MergeFlags(self, args, unique=1): def MergeFlags(self, args, unique=1, dict=None):
""" """
Merge the dict in args into the construction variables. If args Merge the dict in args into the construction variables of this
is not a dict, it is converted into a dict using ParseFlags. env, or the passed-in dict. If args is not a dict, it is
If unique is not set, the flags are appended rather than merged. converted into a dict using ParseFlags. If unique is not set,
the flags are appended rather than merged.
""" """
if dict is None:
dict = self
if not SCons.Util.is_Dict(args): if not SCons.Util.is_Dict(args):
args = self.ParseFlags(args) args = self.ParseFlags(args)
if not unique: if not unique:
@ -763,6 +840,26 @@ class SubstitutionEnvironment:
self[key] = t self[key] = t
return self return self
# def MergeShellPaths(self, args, prepend=1):
# """
# Merge the dict in args into the shell environment in env['ENV'].
# Shell path elements are appended or prepended according to prepend.
# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
# prepends /usr/local/lib to env['ENV']['LIBPATH'].
# """
# for pathname, pathval in args.items():
# if not pathval:
# continue
# if prepend:
# apply(self.PrependENVPath, (pathname, pathval))
# else:
# apply(self.AppendENVPath, (pathname, pathval))
# Used by the FindSourceFiles() method, below. # Used by the FindSourceFiles() method, below.
# Stuck here for support of pre-2.2 Python versions. # Stuck here for support of pre-2.2 Python versions.
def build_source(ss, result): def build_source(ss, result):
@ -819,7 +916,8 @@ class Base(SubstitutionEnvironment):
platform=None, platform=None,
tools=None, tools=None,
toolpath=None, toolpath=None,
options=None, variables=None,
parse_flags = None,
**kw): **kw):
""" """
Initialization of a basic SCons construction environment, Initialization of a basic SCons construction environment,
@ -862,14 +960,19 @@ class Base(SubstitutionEnvironment):
self._dict['PLATFORM'] = str(platform) self._dict['PLATFORM'] = str(platform)
platform(self) platform(self)
# Apply the passed-in variables and customizable options to the # Apply the passed-in and customizable variables to the
# environment before calling the tools, because they may use # environment before calling the tools, because they may use
# some of them during initialization. # some of them during initialization.
if kw.has_key('options'):
# Backwards compatibility: they may stll be using the
# old "options" keyword.
variables = kw['options']
del kw['options']
apply(self.Replace, (), kw) apply(self.Replace, (), kw)
keys = kw.keys() keys = kw.keys()
if options: if variables:
keys = keys + options.keys() keys = keys + variables.keys()
options.Update(self) variables.Update(self)
save = {} save = {}
for k in keys: for k in keys:
@ -888,12 +991,15 @@ class Base(SubstitutionEnvironment):
tools = ['default'] tools = ['default']
apply_tools(self, tools, toolpath) apply_tools(self, tools, toolpath)
# Now restore the passed-in variables and customized options # Now restore the passed-in and customized variables
# to the environment, since the values the user set explicitly # to the environment, since the values the user set explicitly
# should override any values set by the tools. # should override any values set by the tools.
for key, val in save.items(): for key, val in save.items():
self._dict[key] = val self._dict[key] = val
# Finally, apply any flags to be merged in
if parse_flags: self.MergeFlags(parse_flags)
####################################################################### #######################################################################
# Utility methods that are primarily for internal use by SCons. # Utility methods that are primarily for internal use by SCons.
# These begin with lower-case letters. # These begin with lower-case letters.
@ -909,10 +1015,17 @@ class Base(SubstitutionEnvironment):
def get_CacheDir(self): def get_CacheDir(self):
try: try:
return self._CacheDir path = self._CacheDir_path
except AttributeError: except AttributeError:
cd = SCons.Defaults.DefaultEnvironment()._CacheDir path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
self._CacheDir = cd try:
if path == self._last_CacheDir_path:
return self._last_CacheDir
except AttributeError:
pass
cd = SCons.CacheDir.CacheDir(path)
self._last_CacheDir_path = path
self._last_CacheDir = cd
return cd return cd
def get_factory(self, factory, default='File'): def get_factory(self, factory, default='File'):
@ -1085,31 +1198,39 @@ class Base(SubstitutionEnvironment):
orig[val] = None orig[val] = None
self.scanner_map_delete(kw) self.scanner_map_delete(kw)
def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): def AppendENVPath(self, name, newpath, envname = 'ENV',
sep = os.pathsep, delete_existing=1):
"""Append path elements to the path 'name' in the 'ENV' """Append path elements to the path 'name' in the 'ENV'
dictionary for this environment. Will only add any particular dictionary for this environment. Will only add any particular
path once, and will normpath and normcase all paths to help path once, and will normpath and normcase all paths to help
assure this. This can also handle the case where the env assure this. This can also handle the case where the env
variable is a list instead of a string. variable is a list instead of a string.
If delete_existing is 0, a newpath which is already in the path
will not be moved to the end (it will be left where it is).
""" """
orig = '' orig = ''
if self._dict.has_key(envname) and self._dict[envname].has_key(name): if self._dict.has_key(envname) and self._dict[envname].has_key(name):
orig = self._dict[envname][name] orig = self._dict[envname][name]
nv = SCons.Util.AppendPath(orig, newpath, sep) nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing)
if not self._dict.has_key(envname): if not self._dict.has_key(envname):
self._dict[envname] = {} self._dict[envname] = {}
self._dict[envname][name] = nv self._dict[envname][name] = nv
def AppendUnique(self, **kw): def AppendUnique(self, delete_existing=0, **kw):
"""Append values to existing construction variables """Append values to existing construction variables
in an Environment, if they're not already there. in an Environment, if they're not already there.
If delete_existing is 1, removes existing values first, so
values move to end.
""" """
kw = copy_non_reserved_keywords(kw) kw = copy_non_reserved_keywords(kw)
for key, val in kw.items(): for key, val in kw.items():
if SCons.Util.is_List(val):
val = _delete_duplicates(val, delete_existing)
if not self._dict.has_key(key) or self._dict[key] in ('', None): if not self._dict.has_key(key) or self._dict[key] in ('', None):
self._dict[key] = val self._dict[key] = val
elif SCons.Util.is_Dict(self._dict[key]) and \ elif SCons.Util.is_Dict(self._dict[key]) and \
@ -1119,6 +1240,9 @@ class Base(SubstitutionEnvironment):
dk = self._dict[key] dk = self._dict[key]
if not SCons.Util.is_List(dk): if not SCons.Util.is_List(dk):
dk = [dk] dk = [dk]
if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
else:
val = filter(lambda x, dk=dk: x not in dk, val) val = filter(lambda x, dk=dk: x not in dk, val)
self._dict[key] = dk + val self._dict[key] = dk + val
else: else:
@ -1126,13 +1250,19 @@ class Base(SubstitutionEnvironment):
if SCons.Util.is_List(dk): if SCons.Util.is_List(dk):
# By elimination, val is not a list. Since dk is a # By elimination, val is not a list. Since dk is a
# list, wrap val in a list first. # list, wrap val in a list first.
if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = dk + [val]
else:
if not val in dk: if not val in dk:
self._dict[key] = dk + [val] self._dict[key] = dk + [val]
else: else:
self._dict[key] = self._dict[key] + val if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = dk + val
self.scanner_map_delete(kw) self.scanner_map_delete(kw)
def Clone(self, tools=[], toolpath=None, **kw): def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
"""Return a copy of a construction Environment. The """Return a copy of a construction Environment. The
copy is like a Python "deep copy"--that is, independent copy is like a Python "deep copy"--that is, independent
copies are made recursively of each objects--except that copies are made recursively of each objects--except that
@ -1150,8 +1280,13 @@ class Base(SubstitutionEnvironment):
else: else:
clone._dict['BUILDERS'] = BuilderDict(cbd, clone) clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
# Check the methods added via AddMethod() and re-bind them to
# the cloned environment. Only do this if the attribute hasn't
# been overwritten by the user explicitly and still points to
# the added method.
clone.added_methods = [] clone.added_methods = []
for mw in self.added_methods: for mw in self.added_methods:
if mw == getattr(self, mw.name):
clone.added_methods.append(mw.clone(clone)) clone.added_methods.append(mw.clone(clone))
clone._memo = {} clone._memo = {}
@ -1169,10 +1304,18 @@ class Base(SubstitutionEnvironment):
# apply them again in case the tools overwrote them # apply them again in case the tools overwrote them
apply(clone.Replace, (), new) apply(clone.Replace, (), new)
# Finally, apply any flags to be merged in
if parse_flags: clone.MergeFlags(parse_flags)
if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
return clone return clone
def Copy(self, *args, **kw): def Copy(self, *args, **kw):
global _warn_copy_deprecated
if _warn_copy_deprecated:
msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
_warn_copy_deprecated = False
return apply(self.Clone, args, kw) return apply(self.Clone, args, kw)
def _changed_build(self, dependency, target, prev_ni): def _changed_build(self, dependency, target, prev_ni):
@ -1410,31 +1553,39 @@ class Base(SubstitutionEnvironment):
orig[val] = None orig[val] = None
self.scanner_map_delete(kw) self.scanner_map_delete(kw)
def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
delete_existing=1):
"""Prepend path elements to the path 'name' in the 'ENV' """Prepend path elements to the path 'name' in the 'ENV'
dictionary for this environment. Will only add any particular dictionary for this environment. Will only add any particular
path once, and will normpath and normcase all paths to help path once, and will normpath and normcase all paths to help
assure this. This can also handle the case where the env assure this. This can also handle the case where the env
variable is a list instead of a string. variable is a list instead of a string.
If delete_existing is 0, a newpath which is already in the path
will not be moved to the front (it will be left where it is).
""" """
orig = '' orig = ''
if self._dict.has_key(envname) and self._dict[envname].has_key(name): if self._dict.has_key(envname) and self._dict[envname].has_key(name):
orig = self._dict[envname][name] orig = self._dict[envname][name]
nv = SCons.Util.PrependPath(orig, newpath, sep) nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing)
if not self._dict.has_key(envname): if not self._dict.has_key(envname):
self._dict[envname] = {} self._dict[envname] = {}
self._dict[envname][name] = nv self._dict[envname][name] = nv
def PrependUnique(self, **kw): def PrependUnique(self, delete_existing=0, **kw):
"""Append values to existing construction variables """Prepend values to existing construction variables
in an Environment, if they're not already there. in an Environment, if they're not already there.
If delete_existing is 1, removes existing values first, so
values move to front.
""" """
kw = copy_non_reserved_keywords(kw) kw = copy_non_reserved_keywords(kw)
for key, val in kw.items(): for key, val in kw.items():
if SCons.Util.is_List(val):
val = _delete_duplicates(val, not delete_existing)
if not self._dict.has_key(key) or self._dict[key] in ('', None): if not self._dict.has_key(key) or self._dict[key] in ('', None):
self._dict[key] = val self._dict[key] = val
elif SCons.Util.is_Dict(self._dict[key]) and \ elif SCons.Util.is_Dict(self._dict[key]) and \
@ -1444,6 +1595,9 @@ class Base(SubstitutionEnvironment):
dk = self._dict[key] dk = self._dict[key]
if not SCons.Util.is_List(dk): if not SCons.Util.is_List(dk):
dk = [dk] dk = [dk]
if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
else:
val = filter(lambda x, dk=dk: x not in dk, val) val = filter(lambda x, dk=dk: x not in dk, val)
self._dict[key] = val + dk self._dict[key] = val + dk
else: else:
@ -1451,9 +1605,15 @@ class Base(SubstitutionEnvironment):
if SCons.Util.is_List(dk): if SCons.Util.is_List(dk):
# By elimination, val is not a list. Since dk is a # By elimination, val is not a list. Since dk is a
# list, wrap val in a list first. # list, wrap val in a list first.
if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = [val] + dk
else:
if not val in dk: if not val in dk:
self._dict[key] = [val] + dk self._dict[key] = [val] + dk
else: else:
if delete_existing:
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = val + dk self._dict[key] = val + dk
self.scanner_map_delete(kw) self.scanner_map_delete(kw)
@ -1532,6 +1692,7 @@ class Base(SubstitutionEnvironment):
pass pass
elif SCons.Util.is_String(pathext): elif SCons.Util.is_String(pathext):
pathext = self.subst(pathext) pathext = self.subst(pathext)
prog = self.subst(prog)
path = SCons.Util.WhereIs(prog, path, pathext, reject) path = SCons.Util.WhereIs(prog, path, pathext, reject)
if path: return path if path: return path
return None return None
@ -1634,10 +1795,11 @@ class Base(SubstitutionEnvironment):
t.set_always_build() t.set_always_build()
return tlist return tlist
def BuildDir(self, build_dir, src_dir, duplicate=1): def BuildDir(self, *args, **kw):
build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0] if kw.has_key('build_dir'):
src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] kw['variant_dir'] = kw['build_dir']
self.fs.BuildDir(build_dir, src_dir, duplicate) del kw['build_dir']
return apply(self.VariantDir, args, kw)
def Builder(self, **kw): def Builder(self, **kw):
nkw = self.subst_kw(kw) nkw = self.subst_kw(kw)
@ -1645,10 +1807,9 @@ class Base(SubstitutionEnvironment):
def CacheDir(self, path): def CacheDir(self, path):
import SCons.CacheDir import SCons.CacheDir
if path is None: if not path is None:
self._CacheDir = SCons.CacheDir.Null() path = self.subst(path)
else: self._CacheDir_path = path
self._CacheDir = SCons.CacheDir.CacheDir(self.subst(path))
def Clean(self, targets, files): def Clean(self, targets, files):
global CleanTargets global CleanTargets
@ -1699,7 +1860,13 @@ class Base(SubstitutionEnvironment):
def Dir(self, name, *args, **kw): def Dir(self, name, *args, **kw):
""" """
""" """
return apply(self.fs.Dir, (self.subst(name),) + args, kw) s = self.subst(name)
if SCons.Util.is_Sequence(s):
result=[]
for e in s:
result.append(apply(self.fs.Dir, (e,) + args, kw))
return result
return apply(self.fs.Dir, (s,) + args, kw)
def NoClean(self, *targets): def NoClean(self, *targets):
"""Tags a target so that it will not be cleaned by -c""" """Tags a target so that it will not be cleaned by -c"""
@ -1722,7 +1889,13 @@ class Base(SubstitutionEnvironment):
def Entry(self, name, *args, **kw): def Entry(self, name, *args, **kw):
""" """
""" """
return apply(self.fs.Entry, (self.subst(name),) + args, kw) s = self.subst(name)
if SCons.Util.is_Sequence(s):
result=[]
for e in s:
result.append(apply(self.fs.Entry, (e,) + args, kw))
return result
return apply(self.fs.Entry, (s,) + args, kw)
def Environment(self, **kw): def Environment(self, **kw):
return apply(SCons.Environment.Environment, [], self.subst_kw(kw)) return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
@ -1733,6 +1906,10 @@ class Base(SubstitutionEnvironment):
action = apply(self.Action, (action,) + args, kw) action = apply(self.Action, (action,) + args, kw)
result = action([], [], self) result = action([], [], self)
if isinstance(result, SCons.Errors.BuildError): if isinstance(result, SCons.Errors.BuildError):
errstr = result.errstr
if result.filename:
errstr = result.filename + ': ' + errstr
sys.stderr.write("scons: *** %s\n" % errstr)
return result.status return result.status
else: else:
return result return result
@ -1740,7 +1917,13 @@ class Base(SubstitutionEnvironment):
def File(self, name, *args, **kw): def File(self, name, *args, **kw):
""" """
""" """
return apply(self.fs.File, (self.subst(name),) + args, kw) s = self.subst(name)
if SCons.Util.is_Sequence(s):
result=[]
for e in s:
result.append(apply(self.fs.File, (e,) + args, kw))
return result
return apply(self.fs.File, (s,) + args, kw)
def FindFile(self, file, dirs): def FindFile(self, file, dirs):
file = self.subst(file) file = self.subst(file)
@ -1819,6 +2002,11 @@ class Base(SubstitutionEnvironment):
name = self.subst(name) name = self.subst(name)
if not os.path.isabs(name): if not os.path.isabs(name):
name = os.path.join(str(self.fs.SConstruct_dir), name) name = os.path.join(str(self.fs.SConstruct_dir), name)
if name:
name = os.path.normpath(name)
sconsign_dir = os.path.dirname(name)
if sconsign_dir and not os.path.exists(sconsign_dir):
self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
SCons.SConsign.File(name, dbm_module) SCons.SConsign.File(name, dbm_module)
def SideEffect(self, side_effect, target): def SideEffect(self, side_effect, target):
@ -1845,6 +2033,12 @@ class Base(SubstitutionEnvironment):
return entries return entries
def SourceSignatures(self, type): def SourceSignatures(self, type):
global _warn_source_signatures_deprecated
if _warn_source_signatures_deprecated:
msg = "The env.SourceSignatures() method is deprecated;\n" + \
"\tconvert your build to use the env.Decider() method instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
_warn_source_signatures_deprecated = False
type = self.subst(type) type = self.subst(type)
self.src_sig_type = type self.src_sig_type = type
if type == 'MD5': if type == 'MD5':
@ -1875,6 +2069,12 @@ class Base(SubstitutionEnvironment):
return [self.subst(arg)] return [self.subst(arg)]
def TargetSignatures(self, type): def TargetSignatures(self, type):
global _warn_target_signatures_deprecated
if _warn_target_signatures_deprecated:
msg = "The env.TargetSignatures() method is deprecated;\n" + \
"\tconvert your build to use the env.Decider() method instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
_warn_target_signatures_deprecated = False
type = self.subst(type) type = self.subst(type)
self.tgt_sig_type = type self.tgt_sig_type = type
if type in ('MD5', 'content'): if type in ('MD5', 'content'):
@ -1895,6 +2095,11 @@ class Base(SubstitutionEnvironment):
""" """
return SCons.Node.Python.Value(value, built_value) return SCons.Node.Python.Value(value, built_value)
def VariantDir(self, variant_dir, src_dir, duplicate=1):
variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
self.fs.VariantDir(variant_dir, src_dir, duplicate)
def FindSourceFiles(self, node='.'): def FindSourceFiles(self, node='.'):
""" returns a list of all source files. """ returns a list of all source files.
""" """
@ -1978,7 +2183,7 @@ class OverrideEnvironment(Base):
except KeyError: except KeyError:
return self.__dict__['__subject'].__getitem__(key) return self.__dict__['__subject'].__getitem__(key)
def __setitem__(self, key, value): def __setitem__(self, key, value):
if not SCons.Util.is_valid_construction_var(key): if not is_valid_construction_var(key):
raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
self.__dict__['overrides'][key] = value self.__dict__['overrides'][key] = value
def __delitem__(self, key): def __delitem__(self, key):
@ -2007,6 +2212,10 @@ class OverrideEnvironment(Base):
return 1 return 1
except KeyError: except KeyError:
return self.__dict__['__subject'].has_key(key) return self.__dict__['__subject'].has_key(key)
def __contains__(self, key):
if self.__dict__['overrides'].__contains__(key):
return 1
return self.__dict__['__subject'].__contains__(key)
def Dictionary(self): def Dictionary(self):
"""Emulates the items() method of dictionaries.""" """Emulates the items() method of dictionaries."""
d = self.__dict__['__subject'].Dictionary().copy() d = self.__dict__['__subject'].Dictionary().copy()

View file

@ -0,0 +1,198 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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.
#
"""SCons.Errors
This file contains the exception classes used to handle internal
and user errors in SCons.
"""
__revision__ = "src/engine/SCons/Errors.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util
import exceptions
class BuildError(Exception):
""" Errors occuring while building.
BuildError have the following attributes:
Information about the cause of the build error:
-----------------------------------------------
errstr : a description of the error message
status : the return code of the action that caused the build
error. Must be set to a non-zero value even if the
build error is not due to an action returning a
non-zero returned code.
exitstatus : SCons exit status due to this build error.
Must be nonzero unless due to an explicit Exit()
call. Not always the same as status, since
actions return a status code that should be
respected, but SCons typically exits with 2
irrespective of the return value of the failed
action.
filename : The name of the file or directory that caused the
build error. Set to None if no files are associated with
this error. This might be different from the target
being built. For example, failure to create the
directory in which the target file will appear. It
can be None if the error is not due to a particular
filename.
exc_info : Info about exception that caused the build
error. Set to (None, None, None) if this build
error is not due to an exception.
Information about the cause of the location of the error:
---------------------------------------------------------
node : the error occured while building this target node(s)
executor : the executor that caused the build to fail (might
be None if the build failures is not due to the
executor failing)
action : the action that caused the build to fail (might be
None if the build failures is not due to the an
action failure)
command : the command line for the action that caused the
build to fail (might be None if the build failures
is not due to the an action failure)
"""
def __init__(self,
node=None, errstr="Unknown error", status=2, exitstatus=2,
filename=None, executor=None, action=None, command=None,
exc_info=(None, None, None)):
self.errstr = errstr
self.status = status
self.exitstatus = exitstatus
self.filename = filename
self.exc_info = exc_info
self.node = node
self.executor = executor
self.action = action
self.command = command
Exception.__init__(self, node, errstr, status, exitstatus, filename,
executor, action, command, exc_info)
def __str__(self):
if self.filename:
return self.filename + ': ' + self.errstr
else:
return self.errstr
class InternalError(Exception):
pass
class UserError(Exception):
pass
class StopError(Exception):
pass
class EnvironmentError(Exception):
pass
class ExplicitExit(Exception):
def __init__(self, node=None, status=None, *args):
self.node = node
self.status = status
self.exitstatus = status
apply(Exception.__init__, (self,) + args)
def convert_to_BuildError(status, exc_info=None):
"""
Convert any return code a BuildError Exception.
`status' can either be a return code or an Exception.
The buildError.status we set here will normally be
used as the exit status of the "scons" process.
"""
if not exc_info and isinstance(status, Exception):
exc_info = (status.__class__, status, None)
if isinstance(status, BuildError):
buildError = status
buildError.exitstatus = 2 # always exit with 2 on build errors
elif isinstance(status, ExplicitExit):
status = status.status
errstr = 'Explicit exit, status %s' % status
buildError = BuildError(
errstr=errstr,
status=status, # might be 0, OK here
exitstatus=status, # might be 0, OK here
exc_info=exc_info)
# TODO(1.5):
#elif isinstance(status, (StopError, UserError)):
elif isinstance(status, StopError) or isinstance(status, UserError):
buildError = BuildError(
errstr=str(status),
status=2,
exitstatus=2,
exc_info=exc_info)
elif isinstance(status, exceptions.EnvironmentError):
# If an IOError/OSError happens, raise a BuildError.
# Report the name of the file or directory that caused the
# error, which might be different from the target being built
# (for example, failure to create the directory in which the
# target file will appear).
try: filename = status.filename
except AttributeError: filename = None
buildError = BuildError(
errstr=status.strerror,
status=status.errno,
exitstatus=2,
filename=filename,
exc_info=exc_info)
elif isinstance(status, Exception):
buildError = BuildError(
errstr='%s : %s' % (status.__class__.__name__, status),
status=2,
exitstatus=2,
exc_info=exc_info)
elif SCons.Util.is_String(status):
buildError = BuildError(
errstr=status,
status=2,
exitstatus=2)
else:
buildError = BuildError(
errstr="Error %s" % status,
status=status,
exitstatus=2)
#import sys
#sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status))
return buildError

View file

@ -6,7 +6,7 @@ Nodes.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -28,7 +28,7 @@ Nodes.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Executor.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Executor.py 3842 2008/12/20 22:59:52 scons"
import string import string
@ -134,7 +134,11 @@ class Executor:
raise status raise status
elif status: elif status:
msg = "Error %s" % status msg = "Error %s" % status
raise SCons.Errors.BuildError(errstr=msg, executor=self, action=act) raise SCons.Errors.BuildError(
errstr=msg,
node=self.targets,
executor=self,
action=act)
return status return status
# use extra indirection because with new-style objects (Python 2.2 # use extra indirection because with new-style objects (Python 2.2
@ -160,6 +164,16 @@ class Executor:
self.sources_need_sorting = False self.sources_need_sorting = False
return self.sources return self.sources
def prepare(self):
"""
Preparatory checks for whether this Executor can go ahead
and (try to) build its targets.
"""
for s in self.get_sources():
if s.missing():
msg = "Source `%s' not found, needed by target `%s'."
raise SCons.Errors.StopError, msg % (s, self.targets[0])
def add_pre_action(self, action): def add_pre_action(self, action):
self.pre_actions.append(action) self.pre_actions.append(action)
@ -221,28 +235,28 @@ class Executor:
This essentially short-circuits an N*M scan of the sources for This essentially short-circuits an N*M scan of the sources for
each individual target, which is a hell of a lot more efficient. each individual target, which is a hell of a lot more efficient.
""" """
map(lambda N: N.disambiguate(), node_list)
env = self.get_build_env() env = self.get_build_env()
select_specific_scanner = lambda t: (t[0], t[1].select(t[0]))
remove_null_scanners = lambda t: not t[1] is None
add_scanner_path = lambda t, s=self: \
(t[0], t[1], s.get_build_scanner_path(t[1]))
if scanner:
scanner_list = map(lambda n, s=scanner: (n, s), node_list)
else:
kw = self.get_kw()
get_initial_scanners = lambda n, e=env, kw=kw: \
(n, n.get_env_scanner(e, kw))
scanner_list = map(get_initial_scanners, node_list)
scanner_list = filter(remove_null_scanners, scanner_list)
scanner_list = map(select_specific_scanner, scanner_list)
scanner_list = filter(remove_null_scanners, scanner_list)
scanner_path_list = map(add_scanner_path, scanner_list)
deps = [] deps = []
for node, scanner, path in scanner_path_list: if scanner:
for node in node_list:
node.disambiguate()
s = scanner.select(node)
if not s:
continue
path = self.get_build_scanner_path(s)
deps.extend(node.get_implicit_deps(env, s, path))
else:
kw = self.get_kw()
for node in node_list:
node.disambiguate()
scanner = node.get_env_scanner(env, kw)
if not scanner:
continue
scanner = scanner.select(node)
if not scanner:
continue
path = self.get_build_scanner_path(scanner)
deps.extend(node.get_implicit_deps(env, scanner, path)) deps.extend(node.get_implicit_deps(env, scanner, path))
deps.extend(self.get_implicit_deps()) deps.extend(self.get_implicit_deps())
@ -250,11 +264,6 @@ class Executor:
for tgt in self.targets: for tgt in self.targets:
tgt.add_to_implicit(deps) tgt.add_to_implicit(deps)
def get_missing_sources(self):
"""
"""
return filter(lambda s: s.missing(), self.get_sources())
def _get_unignored_sources_key(self, ignore=()): def _get_unignored_sources_key(self, ignore=()):
return tuple(ignore) return tuple(ignore)
@ -317,10 +326,25 @@ class Executor:
result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env)) result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env))
return result return result
nullenv = None
_Executor = Executor def get_NullEnvironment():
"""Use singleton pattern for Null Environments."""
global nullenv
class Null(_Executor): import SCons.Util
class NullEnvironment(SCons.Util.Null):
import SCons.CacheDir
_CacheDir_path = None
_CacheDir = SCons.CacheDir.CacheDir(None)
def get_CacheDir(self):
return self._CacheDir
if not nullenv:
nullenv = NullEnvironment()
return nullenv
class Null:
"""A null Executor, with a null build Environment, that does """A null Executor, with a null build Environment, that does
nothing when the rest of the methods call it. nothing when the rest of the methods call it.
@ -330,20 +354,40 @@ class Null(_Executor):
""" """
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
if __debug__: logInstanceCreation(self, 'Executor.Null') if __debug__: logInstanceCreation(self, 'Executor.Null')
kw['action'] = [] self.targets = kw['targets']
apply(_Executor.__init__, (self,), kw)
def get_build_env(self): def get_build_env(self):
import SCons.Util return get_NullEnvironment()
class NullEnvironment(SCons.Util.Null):
#def get_scanner(self, key):
# return None
#def changed_since_last_build(self, dependency, target, prev_ni):
# return dependency.changed_since_last_buld(target, prev_ni)
def get_CacheDir(self):
import SCons.CacheDir
return SCons.CacheDir.Null()
return NullEnvironment()
def get_build_scanner_path(self): def get_build_scanner_path(self):
return None return None
def cleanup(self): def cleanup(self):
pass pass
def prepare(self):
pass
def get_unignored_sources(self, *args, **kw):
return tuple(())
def get_action_list(self):
return []
def __call__(self, *args, **kw):
return 0
def get_contents(self):
return ''
def _morph(self):
"""Morph this Null executor to a real Executor object."""
self.__class__ = Executor
self.__init__([], targets=self.targets)
# The following methods require morphing this Null Executor to a
# real Executor object.
def add_pre_action(self, action):
self._morph()
self.add_pre_action(action)
def add_post_action(self, action):
self._morph()
self.add_post_action(action)
def set_action_list(self, action):
self._morph()
self.set_action_list(action)

View file

@ -7,7 +7,7 @@ stop, and wait on jobs.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -29,9 +29,37 @@ stop, and wait on jobs.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Job.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Job.py 3842 2008/12/20 22:59:52 scons"
import os
import signal
import SCons.Errors
# The default stack size (in kilobytes) of the threads used to execute
# jobs in parallel.
#
# We use a stack size of 256 kilobytes. The default on some platforms
# is too large and prevents us from creating enough threads to fully
# parallelized the build. For example, the default stack size on linux
# is 8 MBytes.
explicit_stack_size = None
default_stack_size = 256
interrupt_msg = 'Build interrupted.'
class InterruptState:
def __init__(self):
self.interrupted = False
def set(self):
self.interrupted = True
def __call__(self):
return self.interrupted
import SCons.compat
class Jobs: class Jobs:
"""An instance of this class initializes N jobs, and provides """An instance of this class initializes N jobs, and provides
@ -54,8 +82,12 @@ class Jobs:
self.job = None self.job = None
if num > 1: if num > 1:
stack_size = explicit_stack_size
if stack_size is None:
stack_size = default_stack_size
try: try:
self.job = Parallel(taskmaster, num) self.job = Parallel(taskmaster, num, stack_size)
self.num_jobs = num self.num_jobs = num
except NameError: except NameError:
pass pass
@ -63,21 +95,71 @@ class Jobs:
self.job = Serial(taskmaster) self.job = Serial(taskmaster)
self.num_jobs = 1 self.num_jobs = 1
def run(self): def run(self, postfunc=lambda: None):
"""run the job""" """Run the jobs.
postfunc() will be invoked after the jobs has run. It will be
invoked even if the jobs are interrupted by a keyboard
interrupt (well, in fact by a signal such as either SIGINT,
SIGTERM or SIGHUP). The execution of postfunc() is protected
against keyboard interrupts and is guaranteed to run to
completion."""
self._setup_sig_handler()
try: try:
self.job.start() self.job.start()
except KeyboardInterrupt: finally:
# mask any further keyboard interrupts so that scons postfunc()
# can shutdown cleanly: self._reset_sig_handler()
# (this only masks the keyboard interrupt for Python,
# child processes can still get the keyboard interrupt)
import signal
signal.signal(signal.SIGINT, signal.SIG_IGN)
raise
def cleanup(self): def were_interrupted(self):
self.job.cleanup() """Returns whether the jobs were interrupted by a signal."""
return self.job.interrupted()
def _setup_sig_handler(self):
"""Setup an interrupt handler so that SCons can shutdown cleanly in
various conditions:
a) SIGINT: Keyboard interrupt
b) SIGTERM: kill or system shutdown
c) SIGHUP: Controlling shell exiting
We handle all of these cases by stopping the taskmaster. It
turns out that it very difficult to stop the build process
by throwing asynchronously an exception such as
KeyboardInterrupt. For example, the python Condition
variables (threading.Condition) and Queue's do not seem to
asynchronous-exception-safe. It would require adding a whole
bunch of try/finally block and except KeyboardInterrupt all
over the place.
Note also that we have to be careful to handle the case when
SCons forks before executing another process. In that case, we
want the child to exit immediately.
"""
def handler(signum, stack, self=self, parentpid=os.getpid()):
if os.getpid() == parentpid:
self.job.taskmaster.stop()
self.job.interrupted.set()
else:
os._exit(2)
self.old_sigint = signal.signal(signal.SIGINT, handler)
self.old_sigterm = signal.signal(signal.SIGTERM, handler)
try:
self.old_sighup = signal.signal(signal.SIGHUP, handler)
except AttributeError:
pass
def _reset_sig_handler(self):
"""Restore the signal handlers to their previous state (before the
call to _setup_sig_handler()."""
signal.signal(signal.SIGINT, self.old_sigint)
signal.signal(signal.SIGTERM, self.old_sigterm)
try:
signal.signal(signal.SIGHUP, self.old_sighup)
except AttributeError:
pass
class Serial: class Serial:
"""This class is used to execute tasks in series, and is more efficient """This class is used to execute tasks in series, and is more efficient
@ -97,6 +179,7 @@ class Serial:
execute (e.g. execute() raised an exception).""" execute (e.g. execute() raised an exception)."""
self.taskmaster = taskmaster self.taskmaster = taskmaster
self.interrupted = InterruptState()
def start(self): def start(self):
"""Start the job. This will begin pulling tasks from the taskmaster """Start the job. This will begin pulling tasks from the taskmaster
@ -112,11 +195,18 @@ class Serial:
try: try:
task.prepare() task.prepare()
if task.needs_execute():
task.execute() task.execute()
except KeyboardInterrupt: except:
raise if self.interrupted():
try:
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
except: except:
task.exception_set() task.exception_set()
else:
task.exception_set()
# Let the failed() callback function arrange for the # Let the failed() callback function arrange for the
# build to stop if that's appropriate. # build to stop if that's appropriate.
task.failed() task.failed()
@ -124,9 +214,8 @@ class Serial:
task.executed() task.executed()
task.postprocess() task.postprocess()
self.taskmaster.cleanup()
def cleanup(self):
pass
# Trap import failure so that everything in the Job module but the # Trap import failure so that everything in the Job module but the
# Parallel class (and its dependent classes) will work if the interpreter # Parallel class (and its dependent classes) will work if the interpreter
@ -142,28 +231,29 @@ else:
dequeues the task, executes it, and posts a tuple including the task dequeues the task, executes it, and posts a tuple including the task
and a boolean indicating whether the task executed successfully. """ and a boolean indicating whether the task executed successfully. """
def __init__(self, requestQueue, resultsQueue): def __init__(self, requestQueue, resultsQueue, interrupted):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.setDaemon(1) self.setDaemon(1)
self.requestQueue = requestQueue self.requestQueue = requestQueue
self.resultsQueue = resultsQueue self.resultsQueue = resultsQueue
self.interrupted = interrupted
self.start() self.start()
def run(self): def run(self):
while 1: while 1:
task = self.requestQueue.get() task = self.requestQueue.get()
if not task: if task is None:
# The "None" value is used as a sentinel by # The "None" value is used as a sentinel by
# ThreadPool.cleanup(). This indicates that there # ThreadPool.cleanup(). This indicates that there
# are no more tasks, so we should quit. # are no more tasks, so we should quit.
break break
try: try:
if self.interrupted():
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
task.execute() task.execute()
except KeyboardInterrupt:
# be explicit here for test/interrupts.py
ok = False
except: except:
task.exception_set() task.exception_set()
ok = False ok = False
@ -175,27 +265,49 @@ else:
class ThreadPool: class ThreadPool:
"""This class is responsible for spawning and managing worker threads.""" """This class is responsible for spawning and managing worker threads."""
def __init__(self, num): def __init__(self, num, stack_size, interrupted):
"""Create the request and reply queues, and 'num' worker threads.""" """Create the request and reply queues, and 'num' worker threads.
One must specify the stack size of the worker threads. The
stack size is specified in kilobytes.
"""
self.requestQueue = Queue.Queue(0) self.requestQueue = Queue.Queue(0)
self.resultsQueue = Queue.Queue(0) self.resultsQueue = Queue.Queue(0)
try:
prev_size = threading.stack_size(stack_size*1024)
except AttributeError, e:
# Only print a warning if the stack size has been
# explicitly set.
if not explicit_stack_size is None:
msg = "Setting stack size is unsupported by this version of Python:\n " + \
e.args[0]
SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
except ValueError, e:
msg = "Setting stack size failed:\n " + str(e)
SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
# Create worker threads # Create worker threads
self.workers = [] self.workers = []
for _ in range(num): for _ in range(num):
worker = Worker(self.requestQueue, self.resultsQueue) worker = Worker(self.requestQueue, self.resultsQueue, interrupted)
self.workers.append(worker) self.workers.append(worker)
def put(self, obj): # Once we drop Python 1.5 we can change the following to:
#if 'prev_size' in locals():
if 'prev_size' in locals().keys():
threading.stack_size(prev_size)
def put(self, task):
"""Put task into request queue.""" """Put task into request queue."""
self.requestQueue.put(obj) self.requestQueue.put(task)
def get(self, block = True): def get(self):
"""Remove and return a result tuple from the results queue.""" """Remove and return a result tuple from the results queue."""
return self.resultsQueue.get(block) return self.resultsQueue.get()
def preparation_failed(self, obj): def preparation_failed(self, task):
self.resultsQueue.put((obj, False)) self.resultsQueue.put((task, False))
def cleanup(self): def cleanup(self):
""" """
@ -233,7 +345,7 @@ else:
This class is thread safe. This class is thread safe.
""" """
def __init__(self, taskmaster, num): def __init__(self, taskmaster, num, stack_size):
"""Create a new parallel job given a taskmaster. """Create a new parallel job given a taskmaster.
The taskmaster's next_task() method should return the next The taskmaster's next_task() method should return the next
@ -249,7 +361,8 @@ else:
multiple tasks simultaneously. """ multiple tasks simultaneously. """
self.taskmaster = taskmaster self.taskmaster = taskmaster
self.tp = ThreadPool(num) self.interrupted = InterruptState()
self.tp = ThreadPool(num, stack_size, self.interrupted)
self.maxjobs = num self.maxjobs = num
@ -269,22 +382,21 @@ else:
if task is None: if task is None:
break break
# prepare task for execution
try: try:
# prepare task for execution
task.prepare() task.prepare()
except KeyboardInterrupt:
raise
except: except:
# Let the failed() callback function arrange
# for the build to stop if that's appropriate.
task.exception_set() task.exception_set()
self.tp.preparation_failed(task) task.failed()
jobs = jobs + 1 task.postprocess()
continue else:
if task.needs_execute():
# dispatch task # dispatch task
self.tp.put(task) self.tp.put(task)
jobs = jobs + 1 jobs = jobs + 1
else:
task.executed()
task.postprocess()
if not task and not jobs: break if not task and not jobs: break
@ -292,11 +404,20 @@ else:
# back and put the next batch of tasks on the queue. # back and put the next batch of tasks on the queue.
while 1: while 1:
task, ok = self.tp.get() task, ok = self.tp.get()
jobs = jobs - 1 jobs = jobs - 1
if ok: if ok:
task.executed() task.executed()
else: else:
if self.interrupted():
try:
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
except:
task.exception_set()
# Let the failed() callback function arrange
# for the build to stop if that's appropriate.
task.failed() task.failed()
task.postprocess() task.postprocess()
@ -304,5 +425,5 @@ else:
if self.tp.resultsQueue.empty(): if self.tp.resultsQueue.empty():
break break
def cleanup(self):
self.tp.cleanup() self.tp.cleanup()
self.taskmaster.cleanup()

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Memoize.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Memoize.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Memoizer __doc__ = """Memoizer
@ -217,33 +217,47 @@ class Memoizer:
class M: class M:
def __init__(cls, name, bases, cls_dict): def __init__(cls, name, bases, cls_dict):
cls.has_metaclass = 1 cls.use_metaclass = 1
def fake_method(self):
class A: pass
__metaclass__ = M new.instancemethod(fake_method, None, cls)
try: try:
has_metaclass = A.has_metaclass class A:
__metaclass__ = M
use_metaclass = A.use_metaclass
except AttributeError: except AttributeError:
has_metaclass = None use_metaclass = None
reason = 'no metaclasses'
except TypeError:
use_metaclass = None
reason = 'new.instancemethod() bug'
else:
del A
del M del M
del A
if not has_metaclass: if not use_metaclass:
def Dump(title): def Dump(title):
pass pass
class Memoized_Metaclass: try:
class Memoized_Metaclass(type):
# Just a place-holder so pre-metaclass Python versions don't # Just a place-holder so pre-metaclass Python versions don't
# have to have special code for the Memoized classes. # have to have special code for the Memoized classes.
pass pass
except TypeError:
class Memoized_Metaclass:
# A place-holder so pre-metaclass Python versions don't
# have to have special code for the Memoized classes.
pass
def EnableMemoization(): def EnableMemoization():
import SCons.Warnings import SCons.Warnings
msg = 'memoization is not supported in this version of Python (no metaclasses)' msg = 'memoization is not supported in this version of Python (%s)'
raise SCons.Warnings.NoMetaclassSupportWarning, msg raise SCons.Warnings.NoMetaclassSupportWarning, msg % reason
else: else:

View file

@ -8,7 +8,7 @@ This creates a hash of global Aliases (dummy targets).
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ This creates a hash of global Aliases (dummy targets).
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Node/Alias.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Node/Alias.py 3842 2008/12/20 22:59:52 scons"
import string import string
import UserDict import UserDict
@ -74,6 +74,9 @@ class Alias(SCons.Node.Node):
SCons.Node.Node.__init__(self) SCons.Node.Node.__init__(self)
self.name = name self.name = name
def str_for_display(self):
return '"' + self.__str__() + '"'
def __str__(self): def __str__(self):
return self.name return self.name

View file

@ -11,7 +11,7 @@ that can be used by scripts or modules looking for the canonical default.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -33,9 +33,10 @@ that can be used by scripts or modules looking for the canonical default.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Node/FS.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Node/FS.py 3842 2008/12/20 22:59:52 scons"
import fnmatch import fnmatch
from itertools import izip
import os import os
import os.path import os.path
import re import re
@ -58,6 +59,25 @@ import SCons.Warnings
from SCons.Debug import Trace from SCons.Debug import Trace
do_store_info = True
class EntryProxyAttributeError(AttributeError):
"""
An AttributeError subclass for recording and displaying the name
of the underlying Entry involved in an AttributeError exception.
"""
def __init__(self, entry_proxy, attribute):
AttributeError.__init__(self)
self.entry_proxy = entry_proxy
self.attribute = attribute
def __str__(self):
entry = self.entry_proxy.get()
fmt = "%s instance %s has no attribute %s"
return fmt % (entry.__class__.__name__,
repr(entry.name),
repr(self.attribute))
# The max_drift value: by default, use a cached signature value for # The max_drift value: by default, use a cached signature value for
# any file that's been untouched for more than two days. # any file that's been untouched for more than two days.
default_max_drift = 2*24*60*60 default_max_drift = 2*24*60*60
@ -73,10 +93,10 @@ default_max_drift = 2*24*60*60
# #
# A number of the above factors, however, can be set after we've already # A number of the above factors, however, can be set after we've already
# been asked to return a string for a Node, because a Repository() or # been asked to return a string for a Node, because a Repository() or
# BuildDir() call or the like may not occur until later in SConscript # VariantDir() call or the like may not occur until later in SConscript
# files. So this variable controls whether we bother trying to save # files. So this variable controls whether we bother trying to save
# string values for Nodes. The wrapper interface can set this whenever # string values for Nodes. The wrapper interface can set this whenever
# they're done mucking with Repository and BuildDir and the other stuff, # they're done mucking with Repository and VariantDir and the other stuff,
# to let this module know it can start returning saved string values # to let this module know it can start returning saved string values
# for Nodes. # for Nodes.
# #
@ -222,8 +242,6 @@ def LinkFunc(target, source, env):
if func == Link_Funcs[-1]: if func == Link_Funcs[-1]:
# exception of the last link method (copy) are fatal # exception of the last link method (copy) are fatal
raise raise
else:
pass
return 0 return 0
Link = SCons.Action.Action(LinkFunc, None) Link = SCons.Action.Action(LinkFunc, None)
@ -445,7 +463,7 @@ class EntryProxy(SCons.Util.Proxy):
def __get_srcdir(self): def __get_srcdir(self):
"""Returns the directory containing the source node linked to this """Returns the directory containing the source node linked to this
node via BuildDir(), or the directory of this node if not linked.""" node via VariantDir(), or the directory of this node if not linked."""
return EntryProxy(self.get().srcnode().dir) return EntryProxy(self.get().srcnode().dir)
def __get_rsrcnode(self): def __get_rsrcnode(self):
@ -453,7 +471,7 @@ class EntryProxy(SCons.Util.Proxy):
def __get_rsrcdir(self): def __get_rsrcdir(self):
"""Returns the directory containing the source node linked to this """Returns the directory containing the source node linked to this
node via BuildDir(), or the directory of this node if not linked.""" node via VariantDir(), or the directory of this node if not linked."""
return EntryProxy(self.get().srcnode().rfile().dir) return EntryProxy(self.get().srcnode().rfile().dir)
def __get_dir(self): def __get_dir(self):
@ -482,16 +500,11 @@ class EntryProxy(SCons.Util.Proxy):
except KeyError: except KeyError:
try: try:
attr = SCons.Util.Proxy.__getattr__(self, name) attr = SCons.Util.Proxy.__getattr__(self, name)
except AttributeError: except AttributeError, e:
entry = self.get() # Raise our own AttributeError subclass with an
classname = string.split(str(entry.__class__), '.')[-1] # overridden __str__() method that identifies the
if classname[-2:] == "'>": # name of the entry that caused the exception.
# new-style classes report their name as: raise EntryProxyAttributeError(self, name)
# "<class 'something'>"
# instead of the classic classes:
# "something"
classname = classname[:-2]
raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name)
return attr return attr
else: else:
return attr_function(self) return attr_function(self)
@ -543,6 +556,9 @@ class Base(SCons.Node.Node):
self.cwd = None # will hold the SConscript directory for target nodes self.cwd = None # will hold the SConscript directory for target nodes
self.duplicate = directory.duplicate self.duplicate = directory.duplicate
def str_for_display(self):
return '"' + self.__str__() + '"'
def must_be_same(self, klass): def must_be_same(self, klass):
""" """
This node, which already existed, is being looked up as the This node, which already existed, is being looked up as the
@ -586,7 +602,7 @@ class Base(SCons.Node.Node):
if self.duplicate or self.is_derived(): if self.duplicate or self.is_derived():
return self.get_path() return self.get_path()
srcnode = self.srcnode() srcnode = self.srcnode()
if srcnode.stat() is None and not self.stat() is None: if srcnode.stat() is None and self.stat() is not None:
result = self.get_path() result = self.get_path()
else: else:
result = srcnode.get_path() result = srcnode.get_path()
@ -601,7 +617,7 @@ class Base(SCons.Node.Node):
# values that the underlying stat() method saved. # values that the underlying stat() method saved.
try: del self._memo['stat'] try: del self._memo['stat']
except KeyError: pass except KeyError: pass
if not self is srcnode: if self is not srcnode:
try: del srcnode._memo['stat'] try: del srcnode._memo['stat']
except KeyError: pass except KeyError: pass
return result return result
@ -619,7 +635,7 @@ class Base(SCons.Node.Node):
return result return result
def exists(self): def exists(self):
return not self.stat() is None return self.stat() is not None
def rexists(self): def rexists(self):
return self.rfile().exists() return self.rfile().exists()
@ -636,11 +652,11 @@ class Base(SCons.Node.Node):
def isdir(self): def isdir(self):
st = self.stat() st = self.stat()
return not st is None and stat.S_ISDIR(st[stat.ST_MODE]) return st is not None and stat.S_ISDIR(st[stat.ST_MODE])
def isfile(self): def isfile(self):
st = self.stat() st = self.stat()
return not st is None and stat.S_ISREG(st[stat.ST_MODE]) return st is not None and stat.S_ISREG(st[stat.ST_MODE])
if hasattr(os, 'symlink'): if hasattr(os, 'symlink'):
def islink(self): def islink(self):
@ -880,10 +896,10 @@ class Entry(Base):
def must_be_same(self, klass): def must_be_same(self, klass):
"""Called to make sure a Node is a Dir. Since we're an """Called to make sure a Node is a Dir. Since we're an
Entry, we can morph into one.""" Entry, we can morph into one."""
if not self.__class__ is klass: if self.__class__ is not klass:
self.__class__ = klass self.__class__ = klass
self._morph() self._morph()
self.clear self.clear()
# The following methods can get called before the Taskmaster has # The following methods can get called before the Taskmaster has
# had a chance to call disambiguate() directly to see if this Entry # had a chance to call disambiguate() directly to see if this Entry
@ -904,7 +920,7 @@ class Entry(Base):
def rel_path(self, other): def rel_path(self, other):
d = self.disambiguate() d = self.disambiguate()
if d.__class__ == Entry: if d.__class__ is Entry:
raise "rel_path() could not disambiguate File/Dir" raise "rel_path() could not disambiguate File/Dir"
return d.rel_path(other) return d.rel_path(other)
@ -1059,7 +1075,7 @@ class FS(LocalFS):
""" """
curr=self._cwd curr=self._cwd
try: try:
if not dir is None: if dir is not None:
self._cwd = dir self._cwd = dir
if change_os_dir: if change_os_dir:
os.chdir(dir.abspath) os.chdir(dir.abspath)
@ -1165,7 +1181,7 @@ class FS(LocalFS):
return root._lookup_abs(p, fsclass, create) return root._lookup_abs(p, fsclass, create)
def Entry(self, name, directory = None, create = 1): def Entry(self, name, directory = None, create = 1):
"""Lookup or create a generic Entry node with the specified name. """Look up or create a generic Entry node with the specified name.
If the name is a relative path (begins with ./, ../, or a file If the name is a relative path (begins with ./, ../, or a file
name), then it is looked up relative to the supplied directory name), then it is looked up relative to the supplied directory
node, or to the top level directory of the FS (supplied at node, or to the top level directory of the FS (supplied at
@ -1174,7 +1190,7 @@ class FS(LocalFS):
return self._lookup(name, directory, Entry, create) return self._lookup(name, directory, Entry, create)
def File(self, name, directory = None, create = 1): def File(self, name, directory = None, create = 1):
"""Lookup or create a File node with the specified name. If """Look up or create a File node with the specified name. If
the name is a relative path (begins with ./, ../, or a file name), the name is a relative path (begins with ./, ../, or a file name),
then it is looked up relative to the supplied directory node, then it is looked up relative to the supplied directory node,
or to the top level directory of the FS (supplied at construction or to the top level directory of the FS (supplied at construction
@ -1186,7 +1202,7 @@ class FS(LocalFS):
return self._lookup(name, directory, File, create) return self._lookup(name, directory, File, create)
def Dir(self, name, directory = None, create = True): def Dir(self, name, directory = None, create = True):
"""Lookup or create a Dir node with the specified name. If """Look up or create a Dir node with the specified name. If
the name is a relative path (begins with ./, ../, or a file name), the name is a relative path (begins with ./, ../, or a file name),
then it is looked up relative to the supplied directory node, then it is looked up relative to the supplied directory node,
or to the top level directory of the FS (supplied at construction or to the top level directory of the FS (supplied at construction
@ -1197,21 +1213,21 @@ class FS(LocalFS):
""" """
return self._lookup(name, directory, Dir, create) return self._lookup(name, directory, Dir, create)
def BuildDir(self, build_dir, src_dir, duplicate=1): def VariantDir(self, variant_dir, src_dir, duplicate=1):
"""Link the supplied build directory to the source directory """Link the supplied variant directory to the source directory
for purposes of building files.""" for purposes of building files."""
if not isinstance(src_dir, SCons.Node.Node): if not isinstance(src_dir, SCons.Node.Node):
src_dir = self.Dir(src_dir) src_dir = self.Dir(src_dir)
if not isinstance(build_dir, SCons.Node.Node): if not isinstance(variant_dir, SCons.Node.Node):
build_dir = self.Dir(build_dir) variant_dir = self.Dir(variant_dir)
if src_dir.is_under(build_dir): if src_dir.is_under(variant_dir):
raise SCons.Errors.UserError, "Source directory cannot be under build directory." raise SCons.Errors.UserError, "Source directory cannot be under variant directory."
if build_dir.srcdir: if variant_dir.srcdir:
if build_dir.srcdir == src_dir: if variant_dir.srcdir == src_dir:
return # We already did this. return # We already did this.
raise SCons.Errors.UserError, "'%s' already has a source directory: '%s'."%(build_dir, build_dir.srcdir) raise SCons.Errors.UserError, "'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)
build_dir.link(src_dir, duplicate) variant_dir.link(src_dir, duplicate)
def Repository(self, *dirs): def Repository(self, *dirs):
"""Specify Repository directories to search.""" """Specify Repository directories to search."""
@ -1220,11 +1236,11 @@ class FS(LocalFS):
d = self.Dir(d) d = self.Dir(d)
self.Top.addRepository(d) self.Top.addRepository(d)
def build_dir_target_climb(self, orig, dir, tail): def variant_dir_target_climb(self, orig, dir, tail):
"""Create targets in corresponding build directories """Create targets in corresponding variant directories
Climb the directory tree, and look up path names Climb the directory tree, and look up path names
relative to any linked build directories we find. relative to any linked variant directories we find.
Even though this loops and walks up the tree, we don't memoize Even though this loops and walks up the tree, we don't memoize
the return value because this is really only used to process the return value because this is really only used to process
@ -1232,10 +1248,10 @@ class FS(LocalFS):
""" """
targets = [] targets = []
message = None message = None
fmt = "building associated BuildDir targets: %s" fmt = "building associated VariantDir targets: %s"
start_dir = dir start_dir = dir
while dir: while dir:
for bd in dir.build_dirs: for bd in dir.variant_dirs:
if start_dir.is_under(bd): if start_dir.is_under(bd):
# If already in the build-dir location, don't reflect # If already in the build-dir location, don't reflect
return [orig], fmt % str(orig) return [orig], fmt % str(orig)
@ -1314,7 +1330,7 @@ class Dir(Base):
self.cwd = self self.cwd = self
self.searched = 0 self.searched = 0
self._sconsign = None self._sconsign = None
self.build_dirs = [] self.variant_dirs = []
self.root = self.dir.root self.root = self.dir.root
# Don't just reset the executor, replace its action list, # Don't just reset the executor, replace its action list,
@ -1342,7 +1358,7 @@ class Dir(Base):
del node._srcreps del node._srcreps
except AttributeError: except AttributeError:
pass pass
if duplicate != None: if duplicate is not None:
node.duplicate=duplicate node.duplicate=duplicate
def __resetDuplicate(self, node): def __resetDuplicate(self, node):
@ -1361,8 +1377,7 @@ class Dir(Base):
Looks up or creates a directory node named 'name' relative to Looks up or creates a directory node named 'name' relative to
this directory. this directory.
""" """
dir = self.fs.Dir(name, self, create) return self.fs.Dir(name, self, create)
return dir
def File(self, name): def File(self, name):
""" """
@ -1385,16 +1400,16 @@ class Dir(Base):
a path containing '..'), an absolute path name, a top-relative a path containing '..'), an absolute path name, a top-relative
('#foo') path name, or any kind of object. ('#foo') path name, or any kind of object.
""" """
name = self.labspath + '/' + name name = self.entry_labspath(name)
return self.root._lookup_abs(name, klass, create) return self.root._lookup_abs(name, klass, create)
def link(self, srcdir, duplicate): def link(self, srcdir, duplicate):
"""Set this directory as the build directory for the """Set this directory as the variant directory for the
supplied source directory.""" supplied source directory."""
self.srcdir = srcdir self.srcdir = srcdir
self.duplicate = duplicate self.duplicate = duplicate
self.__clearRepositoryCache(duplicate) self.__clearRepositoryCache(duplicate)
srcdir.build_dirs.append(self) srcdir.variant_dirs.append(self)
def getRepositories(self): def getRepositories(self):
"""Returns a list of repositories for this directory. """Returns a list of repositories for this directory.
@ -1407,7 +1422,7 @@ class Dir(Base):
def get_all_rdirs(self): def get_all_rdirs(self):
try: try:
return self._memo['get_all_rdirs'] return list(self._memo['get_all_rdirs'])
except KeyError: except KeyError:
pass pass
@ -1423,7 +1438,7 @@ class Dir(Base):
fname = dir.name + os.sep + fname fname = dir.name + os.sep + fname
dir = dir.up() dir = dir.up()
self._memo['get_all_rdirs'] = result self._memo['get_all_rdirs'] = list(result)
return result return result
@ -1467,11 +1482,9 @@ class Dir(Base):
pass pass
if self is other: if self is other:
result = '.' result = '.'
elif not other in self.path_elements: elif not other in self.path_elements:
try: try:
other_dir = other.get_dir() other_dir = other.get_dir()
except AttributeError: except AttributeError:
@ -1485,9 +1498,7 @@ class Dir(Base):
result = other.name result = other.name
else: else:
result = dir_rel_path + os.sep + other.name result = dir_rel_path + os.sep + other.name
else: else:
i = self.path_elements.index(other) + 1 i = self.path_elements.index(other) + 1
path_elems = ['..'] * (len(self.path_elements) - i) \ path_elems = ['..'] * (len(self.path_elements) - i) \
@ -1538,7 +1549,7 @@ class Dir(Base):
def build(self, **kw): def build(self, **kw):
"""A null "builder" for directories.""" """A null "builder" for directories."""
global MkdirBuilder global MkdirBuilder
if not self.builder is MkdirBuilder: if self.builder is not MkdirBuilder:
apply(SCons.Node.Node.build, [self,], kw) apply(SCons.Node.Node.build, [self,], kw)
# #
@ -1554,10 +1565,9 @@ class Dir(Base):
if parent.exists(): if parent.exists():
break break
listDirs.append(parent) listDirs.append(parent)
p = parent.up() parent = parent.up()
if p is None: else:
raise SCons.Errors.StopError, parent.path raise SCons.Errors.StopError, parent.path
parent = p
listDirs.reverse() listDirs.reverse()
for dirnode in listDirs: for dirnode in listDirs:
try: try:
@ -1577,22 +1587,37 @@ class Dir(Base):
def multiple_side_effect_has_builder(self): def multiple_side_effect_has_builder(self):
global MkdirBuilder global MkdirBuilder
return not self.builder is MkdirBuilder and self.has_builder() return self.builder is not MkdirBuilder and self.has_builder()
def alter_targets(self): def alter_targets(self):
"""Return any corresponding targets in a build directory. """Return any corresponding targets in a variant directory.
""" """
return self.fs.build_dir_target_climb(self, self, []) return self.fs.variant_dir_target_climb(self, self, [])
def scanner_key(self): def scanner_key(self):
"""A directory does not get scanned.""" """A directory does not get scanned."""
return None return None
def get_contents(self): def get_contents(self):
"""Return aggregate contents of all our children.""" """Return content signatures and names of all our children
contents = map(lambda n: n.get_contents(), self.children()) separated by new-lines. Ensure that the nodes are sorted."""
contents = []
name_cmp = lambda a, b: cmp(a.name, b.name)
sorted_children = self.children()[:]
sorted_children.sort(name_cmp)
for node in sorted_children:
contents.append('%s %s\n' % (node.get_csig(), node.name))
return string.join(contents, '') return string.join(contents, '')
def get_csig(self):
"""Compute the content signature for Directory nodes. In
general, this is not needed and the content signature is not
stored in the DirNodeInfo. However, if get_contents on a Dir
node is called which has a child directory, the child
directory should return the hash of its contents."""
contents = self.get_contents()
return SCons.Util.MD5signature(contents)
def do_duplicate(self, src): def do_duplicate(self, src):
pass pass
@ -1601,7 +1626,7 @@ class Dir(Base):
def is_up_to_date(self): def is_up_to_date(self):
"""If any child is not up-to-date, then this directory isn't, """If any child is not up-to-date, then this directory isn't,
either.""" either."""
if not self.builder is MkdirBuilder and not self.exists(): if self.builder is not MkdirBuilder and not self.exists():
return 0 return 0
up_to_date = SCons.Node.up_to_date up_to_date = SCons.Node.up_to_date
for kid in self.children(): for kid in self.children():
@ -1696,7 +1721,7 @@ class Dir(Base):
for dir in self.srcdir_list(): for dir in self.srcdir_list():
if self.is_under(dir): if self.is_under(dir):
# We shouldn't source from something in the build path; # We shouldn't source from something in the build path;
# build_dir is probably under src_dir, in which case # variant_dir is probably under src_dir, in which case
# we are reflecting. # we are reflecting.
break break
if dir.entry_exists_on_disk(name): if dir.entry_exists_on_disk(name):
@ -1761,7 +1786,10 @@ class Dir(Base):
if self.entry_exists_on_disk(name): if self.entry_exists_on_disk(name):
try: return self.Dir(name) try: return self.Dir(name)
except TypeError: pass except TypeError: pass
node = self.srcdir_duplicate(name)
if isinstance(node, File):
return None return None
return node
def file_on_disk(self, name): def file_on_disk(self, name):
if self.entry_exists_on_disk(name) or \ if self.entry_exists_on_disk(name) or \
@ -1771,7 +1799,7 @@ class Dir(Base):
except TypeError: pass except TypeError: pass
node = self.srcdir_duplicate(name) node = self.srcdir_duplicate(name)
if isinstance(node, Dir): if isinstance(node, Dir):
node = None return None
return node return node
def walk(self, func, arg): def walk(self, func, arg):
@ -1823,8 +1851,8 @@ class Dir(Base):
The "source" argument, when true, specifies that corresponding The "source" argument, when true, specifies that corresponding
source Nodes must be returned if you're globbing in a build source Nodes must be returned if you're globbing in a build
directory (initialized with BuildDir()). The default behavior directory (initialized with VariantDir()). The default behavior
is to return Nodes local to the BuildDir(). is to return Nodes local to the VariantDir().
The "strings" argument, when true, returns the matches as strings, The "strings" argument, when true, returns the matches as strings,
not Nodes. The strings are path names relative to this directory. not Nodes. The strings are path names relative to this directory.
@ -1846,6 +1874,7 @@ class Dir(Base):
if strings: if strings:
r = map(lambda x, d=str(dir): os.path.join(d, x), r) r = map(lambda x, d=str(dir): os.path.join(d, x), r)
result.extend(r) result.extend(r)
result.sort(lambda a, b: cmp(str(a), str(b)))
return result return result
def _glob1(self, pattern, ondisk=True, source=False, strings=False): def _glob1(self, pattern, ondisk=True, source=False, strings=False):
@ -1863,6 +1892,7 @@ class Dir(Base):
for srcdir in self.srcdir_list(): for srcdir in self.srcdir_list():
search_dir_list.extend(srcdir.get_all_rdirs()) search_dir_list.extend(srcdir.get_all_rdirs())
selfEntry = self.Entry
names = [] names = []
for dir in search_dir_list: for dir in search_dir_list:
# We use the .name attribute from the Node because the keys of # We use the .name attribute from the Node because the keys of
@ -1872,12 +1902,15 @@ class Dir(Base):
entry_names = filter(lambda n: n not in ('.', '..'), dir.entries.keys()) entry_names = filter(lambda n: n not in ('.', '..'), dir.entries.keys())
node_names = map(lambda n, e=dir.entries: e[n].name, entry_names) node_names = map(lambda n, e=dir.entries: e[n].name, entry_names)
names.extend(node_names) names.extend(node_names)
if not strings:
# Make sure the working directory (self) actually has
# entries for all Nodes in repositories or variant dirs.
map(selfEntry, node_names)
if ondisk: if ondisk:
try: try:
disk_names = os.listdir(dir.abspath) disk_names = os.listdir(dir.abspath)
except os.error: except os.error:
pass continue
else:
names.extend(disk_names) names.extend(disk_names)
if not strings: if not strings:
# We're going to return corresponding Nodes in # We're going to return corresponding Nodes in
@ -1892,11 +1925,13 @@ class Dir(Base):
#disk_names = [ d for d in disk_names if d[0] != '.' ] #disk_names = [ d for d in disk_names if d[0] != '.' ]
disk_names = filter(lambda x: x[0] != '.', disk_names) disk_names = filter(lambda x: x[0] != '.', disk_names)
disk_names = fnmatch.filter(disk_names, pattern) disk_names = fnmatch.filter(disk_names, pattern)
rep_nodes = map(dir.Entry, disk_names) dirEntry = dir.Entry
#rep_nodes = [ n.disambiguate() for n in rep_nodes ] for name in disk_names:
rep_nodes = map(lambda n: n.disambiguate(), rep_nodes) # Add './' before disk filename so that '#' at
for node, name in zip(rep_nodes, disk_names): # beginning of filename isn't interpreted.
n = self.Entry(name) name = './' + name
node = dirEntry(name).disambiguate()
n = selfEntry(name)
if n.__class__ != node.__class__: if n.__class__ != node.__class__:
n.__class__ = node.__class__ n.__class__ = node.__class__
n._morph() n._morph()
@ -1940,7 +1975,7 @@ class RootDir(Dir):
# except for the "lookup abspath," which does not have the # except for the "lookup abspath," which does not have the
# drive letter. # drive letter.
self.abspath = name + os.sep self.abspath = name + os.sep
self.labspath = '/' self.labspath = ''
self.path = name + os.sep self.path = name + os.sep
self.tpath = name + os.sep self.tpath = name + os.sep
self._morph() self._morph()
@ -1951,6 +1986,7 @@ class RootDir(Dir):
# os.path.normpath() seems to preserve double slashes at the # os.path.normpath() seems to preserve double slashes at the
# beginning of a path (presumably for UNC path names), but # beginning of a path (presumably for UNC path names), but
# collapses triple slashes to a single slash. # collapses triple slashes to a single slash.
self._lookupDict[''] = self
self._lookupDict['/'] = self self._lookupDict['/'] = self
self._lookupDict['//'] = self self._lookupDict['//'] = self
self._lookupDict[os.sep] = self self._lookupDict[os.sep] = self
@ -1988,13 +2024,14 @@ class RootDir(Dir):
dir_name, file_name = os.path.split(p) dir_name, file_name = os.path.split(p)
dir_node = self._lookup_abs(dir_name, Dir) dir_node = self._lookup_abs(dir_name, Dir)
result = klass(file_name, dir_node, self.fs) result = klass(file_name, dir_node, self.fs)
self._lookupDict[k] = result
dir_node.entries[_my_normcase(file_name)] = result
dir_node.implicit = None
# Double-check on disk (as configured) that the Node we # Double-check on disk (as configured) that the Node we
# created matches whatever is out there in the real world. # created matches whatever is out there in the real world.
result.diskcheck_match() result.diskcheck_match()
self._lookupDict[k] = result
dir_node.entries[_my_normcase(file_name)] = result
dir_node.implicit = None
else: else:
# There is already a Node for this path name. Allow it to # There is already a Node for this path name. Allow it to
# complain if we were looking for an inappropriate type. # complain if we were looking for an inappropriate type.
@ -2008,7 +2045,7 @@ class RootDir(Dir):
return self.abspath + name return self.abspath + name
def entry_labspath(self, name): def entry_labspath(self, name):
return self.labspath + name return '/' + name
def entry_path(self, name): def entry_path(self, name):
return self.path + name return self.path + name
@ -2106,10 +2143,9 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
strings = getattr(self, nattr) strings = getattr(self, nattr)
nodeinfos = getattr(self, sattr) nodeinfos = getattr(self, sattr)
except AttributeError: except AttributeError:
pass continue
else:
nodes = [] nodes = []
for s, ni in zip(strings, nodeinfos): for s, ni in izip(strings, nodeinfos):
if not isinstance(s, SCons.Node.Node): if not isinstance(s, SCons.Node.Node):
s = ni.str_to_node(s) s = ni.str_to_node(s)
nodes.append(s) nodes.append(s)
@ -2118,7 +2154,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
result = [] result = []
bkids = self.bsources + self.bdepends + self.bimplicit bkids = self.bsources + self.bdepends + self.bimplicit
bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs
for bkid, bkidsig in zip(bkids, bkidsigs): for bkid, bkidsig in izip(bkids, bkidsigs):
result.append(str(bkid) + ': ' + result.append(str(bkid) + ': ' +
string.join(bkidsig.format(names=names), ' ')) string.join(bkidsig.format(names=names), ' '))
result.append('%s [%s]' % (self.bactsig, self.bact)) result.append('%s [%s]' % (self.bactsig, self.bact))
@ -2133,6 +2169,8 @@ class File(Base):
NodeInfo = FileNodeInfo NodeInfo = FileNodeInfo
BuildInfo = FileBuildInfo BuildInfo = FileBuildInfo
md5_chunksize = 64
def diskcheck_match(self): def diskcheck_match(self):
diskcheck_match(self, self.isdir, diskcheck_match(self, self.isdir,
"Directory %s found where file expected.") "Directory %s found where file expected.")
@ -2144,23 +2182,25 @@ class File(Base):
def Entry(self, name): def Entry(self, name):
"""Create an entry node named 'name' relative to """Create an entry node named 'name' relative to
the SConscript directory of this file.""" the directory of this file."""
return self.cwd.Entry(name) return self.dir.Entry(name)
def Dir(self, name, create=True): def Dir(self, name, create=True):
"""Create a directory node named 'name' relative to """Create a directory node named 'name' relative to
the SConscript directory of this file.""" the directory of this file."""
return self.cwd.Dir(name, create) return self.dir.Dir(name, create=create)
def Dirs(self, pathlist): def Dirs(self, pathlist):
"""Create a list of directories relative to the SConscript """Create a list of directories relative to the SConscript
directory of this file.""" directory of this file."""
# TODO(1.5)
# return [self.Dir(p) for p in pathlist]
return map(lambda p, s=self: s.Dir(p), pathlist) return map(lambda p, s=self: s.Dir(p), pathlist)
def File(self, name): def File(self, name):
"""Create a file node named 'name' relative to """Create a file node named 'name' relative to
the SConscript directory of this file.""" the directory of this file."""
return self.cwd.File(name) return self.dir.File(name)
#def generate_build_dict(self): #def generate_build_dict(self):
# """Return an appropriate dictionary of values for building # """Return an appropriate dictionary of values for building
@ -2203,6 +2243,23 @@ class File(Base):
raise raise
return r return r
def get_content_hash(self):
"""
Compute and return the MD5 hash for this file.
"""
if not self.rexists():
return SCons.Util.MD5signature('')
fname = self.rfile().abspath
try:
cs = SCons.Util.MD5filesignature(fname,
chunksize=SCons.Node.FS.File.md5_chunksize*1024)
except EnvironmentError, e:
if not e.filename:
e.filename = fname
raise
return cs
memoizer_counters.append(SCons.Memoize.CountValue('get_size')) memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
def get_size(self): def get_size(self):
@ -2242,6 +2299,7 @@ class File(Base):
# This accomodates "chained builds" where a file that's a target # This accomodates "chained builds" where a file that's a target
# in one build (SConstruct file) is a source in a different build. # in one build (SConstruct file) is a source in a different build.
# See test/chained-build.py for the use case. # See test/chained-build.py for the use case.
if do_store_info:
self.dir.sconsign().store_info(self.name, self) self.dir.sconsign().store_info(self.name, self)
convert_copy_attrs = [ convert_copy_attrs = [
@ -2336,16 +2394,14 @@ class File(Base):
try: try:
value = getattr(old_entry, attr) value = getattr(old_entry, attr)
except AttributeError: except AttributeError:
pass continue
else:
setattr(binfo, attr, value) setattr(binfo, attr, value)
delattr(old_entry, attr) delattr(old_entry, attr)
for attr in self.convert_sig_attrs: for attr in self.convert_sig_attrs:
try: try:
sig_list = getattr(old_entry, attr) sig_list = getattr(old_entry, attr)
except AttributeError: except AttributeError:
pass continue
else:
value = [] value = []
for sig in sig_list: for sig in sig_list:
ninfo = self.new_ninfo() ninfo = self.new_ninfo()
@ -2368,7 +2424,7 @@ class File(Base):
try: try:
sconsign_entry = self.dir.sconsign().get_entry(self.name) sconsign_entry = self.dir.sconsign().get_entry(self.name)
except (KeyError, OSError): except (KeyError, EnvironmentError):
import SCons.SConsign import SCons.SConsign
sconsign_entry = SCons.SConsign.SConsignEntry() sconsign_entry = SCons.SConsign.SConsignEntry()
sconsign_entry.binfo = self.new_binfo() sconsign_entry.binfo = self.new_binfo()
@ -2419,6 +2475,7 @@ class File(Base):
pass pass
if scanner: if scanner:
# result = [n.disambiguate() for n in scanner(self, env, path)]
result = scanner(self, env, path) result = scanner(self, env, path)
result = map(lambda N: N.disambiguate(), result) result = map(lambda N: N.disambiguate(), result)
else: else:
@ -2469,38 +2526,20 @@ class File(Base):
self.get_build_env().get_CacheDir().push_if_forced(self) self.get_build_env().get_CacheDir().push_if_forced(self)
ninfo = self.get_ninfo() ninfo = self.get_ninfo()
old = self.get_stored_info()
csig = None
mtime = self.get_timestamp()
size = self.get_size()
max_drift = self.fs.max_drift
if max_drift > 0:
if (time.time() - mtime) > max_drift:
try:
n = old.ninfo
if n.timestamp and n.csig and n.timestamp == mtime:
csig = n.csig
except AttributeError:
pass
elif max_drift == 0:
try:
csig = old.ninfo.csig
except AttributeError:
pass
csig = self.get_max_drift_csig()
if csig: if csig:
ninfo.csig = csig ninfo.csig = csig
ninfo.timestamp = mtime ninfo.timestamp = self.get_timestamp()
ninfo.size = size ninfo.size = self.get_size()
if not self.has_builder(): if not self.has_builder():
# This is a source file, but it might have been a target file # This is a source file, but it might have been a target file
# in another build that included more of the DAG. Copy # in another build that included more of the DAG. Copy
# any build information that's stored in the .sconsign file # any build information that's stored in the .sconsign file
# into our binfo object so it doesn't get lost. # into our binfo object so it doesn't get lost.
old = self.get_stored_info()
self.get_binfo().__dict__.update(old.binfo.__dict__) self.get_binfo().__dict__.update(old.binfo.__dict__)
self.store_info() self.store_info()
@ -2540,14 +2579,14 @@ class File(Base):
scb = self.sbuilder scb = self.sbuilder
except AttributeError: except AttributeError:
scb = self.sbuilder = self.find_src_builder() scb = self.sbuilder = self.find_src_builder()
return not scb is None return scb is not None
def alter_targets(self): def alter_targets(self):
"""Return any corresponding targets in a build directory. """Return any corresponding targets in a variant directory.
""" """
if self.is_derived(): if self.is_derived():
return [], None return [], None
return self.fs.build_dir_target_climb(self, self.dir, [self.name]) return self.fs.variant_dir_target_climb(self, self.dir, [self.name])
def _rmv_existing(self): def _rmv_existing(self):
self.clear_memoized_values() self.clear_memoized_values()
@ -2613,8 +2652,8 @@ class File(Base):
# Duplicate from source path if we are set up to do this. # Duplicate from source path if we are set up to do this.
if self.duplicate and not self.is_derived() and not self.linked: if self.duplicate and not self.is_derived() and not self.linked:
src = self.srcnode() src = self.srcnode()
if not src is self: if src is not self:
# At this point, src is meant to be copied in a build directory. # At this point, src is meant to be copied in a variant directory.
src = src.rfile() src = src.rfile()
if src.abspath != self.abspath: if src.abspath != self.abspath:
if src.exists(): if src.exists():
@ -2623,7 +2662,7 @@ class File(Base):
# not actually occur if the -n option is being used. # not actually occur if the -n option is being used.
else: else:
# The source file does not exist. Make sure no old # The source file does not exist. Make sure no old
# copy remains in the build directory. # copy remains in the variant directory.
if Base.exists(self) or self.islink(): if Base.exists(self) or self.islink():
self.fs.unlink(self.path) self.fs.unlink(self.path)
# Return None explicitly because the Base.exists() call # Return None explicitly because the Base.exists() call
@ -2638,6 +2677,32 @@ class File(Base):
# SIGNATURE SUBSYSTEM # SIGNATURE SUBSYSTEM
# #
def get_max_drift_csig(self):
"""
Returns the content signature currently stored for this node
if it's been unmodified longer than the max_drift value, or the
max_drift value is 0. Returns None otherwise.
"""
old = self.get_stored_info()
mtime = self.get_timestamp()
max_drift = self.fs.max_drift
if max_drift > 0:
if (time.time() - mtime) > max_drift:
try:
n = old.ninfo
if n.timestamp and n.csig and n.timestamp == mtime:
return n.csig
except AttributeError:
pass
elif max_drift == 0:
try:
return old.ninfo.csig
except AttributeError:
pass
return None
def get_csig(self): def get_csig(self):
""" """
Generate a node's content signature, the digested signature Generate a node's content signature, the digested signature
@ -2653,8 +2718,14 @@ class File(Base):
except AttributeError: except AttributeError:
pass pass
csig = self.get_max_drift_csig()
if csig is None:
try: try:
if self.get_size() < SCons.Node.FS.File.md5_chunksize:
contents = self.get_contents() contents = self.get_contents()
else:
csig = self.get_content_hash()
except IOError: except IOError:
# This can happen if there's actually a directory on-disk, # This can happen if there's actually a directory on-disk,
# which can be the case if they've disabled disk checks, # which can be the case if they've disabled disk checks,
@ -2662,6 +2733,7 @@ class File(Base):
# create a same-named directory by mistake. # create a same-named directory by mistake.
csig = '' csig = ''
else: else:
if not csig:
csig = SCons.Util.MD5signature(contents) csig = SCons.Util.MD5signature(contents)
ninfo.csig = csig ninfo.csig = csig
@ -2684,7 +2756,7 @@ class File(Base):
return 1 return 1
def changed_state(self, target, prev_ni): def changed_state(self, target, prev_ni):
return (self.state != SCons.Node.up_to_date) return self.state != SCons.Node.up_to_date
def changed_timestamp_then_content(self, target, prev_ni): def changed_timestamp_then_content(self, target, prev_ni):
if not self.changed_timestamp_match(target, prev_ni): if not self.changed_timestamp_match(target, prev_ni):
@ -2790,8 +2862,8 @@ class File(Base):
cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self)
if not self.exists() and cachefile and os.path.exists(cachefile): if not self.exists() and cachefile and os.path.exists(cachefile):
contents = open(cachefile, 'rb').read() self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \
self.cachedir_csig = SCons.Util.MD5signature(contents) SCons.Node.FS.File.md5_chunksize * 1024)
else: else:
self.cachedir_csig = self.get_csig() self.cachedir_csig = self.get_csig()
return self.cachedir_csig return self.cachedir_csig
@ -2806,12 +2878,14 @@ class File(Base):
# targets built by the same action will all have the same # targets built by the same action will all have the same
# build signature, and we have to differentiate them somehow. # build signature, and we have to differentiate them somehow.
children = self.children() children = self.children()
sigs = map(lambda n: n.get_cachedir_csig(), children)
executor = self.get_executor() executor = self.get_executor()
# sigs = [n.get_cachedir_csig() for n in children]
sigs = map(lambda n: n.get_cachedir_csig(), children)
sigs.append(SCons.Util.MD5signature(executor.get_contents())) sigs.append(SCons.Util.MD5signature(executor.get_contents()))
sigs.append(self.path) sigs.append(self.path)
self.cachesig = SCons.Util.MD5collect(sigs) result = self.cachesig = SCons.Util.MD5collect(sigs)
return self.cachesig return result
default_fs = None default_fs = None
@ -2842,14 +2916,14 @@ class FileFinder:
It would be more compact to just use this as a nested function It would be more compact to just use this as a nested function
with a default keyword argument (see the commented-out version with a default keyword argument (see the commented-out version
below), but that doesn't work unless you have nested scopes, below), but that doesn't work unless you have nested scopes,
so we define it here just this works work under Python 1.5.2. so we define it here just so this work under Python 1.5.2.
""" """
if fd is None: if fd is None:
fd = self.default_filedir fd = self.default_filedir
dir, name = os.path.split(fd) dir, name = os.path.split(fd)
drive, d = os.path.splitdrive(dir) drive, d = os.path.splitdrive(dir)
if d in ('/', os.sep): if d in ('/', os.sep):
return p return p.fs.get_root(drive).dir_on_disk(name)
if dir: if dir:
p = self.filedir_lookup(p, dir) p = self.filedir_lookup(p, dir)
if not p: if not p:
@ -2859,9 +2933,10 @@ class FileFinder:
node = p.entries[norm_name] node = p.entries[norm_name]
except KeyError: except KeyError:
return p.dir_on_disk(name) return p.dir_on_disk(name)
# Once we move to Python 2.2 we can do: if isinstance(node, Dir):
#if isinstance(node, (Dir, Entry)): return node
if isinstance(node, Dir) or isinstance(node, Entry): if isinstance(node, Entry):
node.must_be_same(Dir)
return node return node
return None return None
@ -2899,14 +2974,11 @@ class FileFinder:
except KeyError: except KeyError:
pass pass
if verbose: if verbose and not callable(verbose):
if not SCons.Util.is_String(verbose): if not SCons.Util.is_String(verbose):
verbose = "find_file" verbose = "find_file"
if not callable(verbose):
verbose = ' %s: ' % verbose verbose = ' %s: ' % verbose
verbose = lambda s, v=verbose: sys.stdout.write(v + s) verbose = lambda s, v=verbose: sys.stdout.write(v + s)
else:
verbose = lambda x: x
filedir, filename = os.path.split(filename) filedir, filename = os.path.split(filename)
if filedir: if filedir:
@ -2930,8 +3002,11 @@ class FileFinder:
# node = p.entries[norm_name] # node = p.entries[norm_name]
# except KeyError: # except KeyError:
# return p.dir_on_disk(name) # return p.dir_on_disk(name)
# # Once we move to Python 2.2 we can do: # if isinstance(node, Dir):
# #if isinstance(node, (Dir, Entry)): # return node
# if isinstance(node, Entry):
# node.must_be_same(Dir)
# return node
# if isinstance(node, Dir) or isinstance(node, Entry): # if isinstance(node, Dir) or isinstance(node, Entry):
# return node # return node
# return None # return None
@ -2942,9 +3017,11 @@ class FileFinder:
result = None result = None
for dir in paths: for dir in paths:
if verbose:
verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) verbose("looking for '%s' in '%s' ...\n" % (filename, dir))
node, d = dir.srcdir_find_file(filename) node, d = dir.srcdir_find_file(filename)
if node: if node:
if verbose:
verbose("... FOUND '%s' in '%s'\n" % (filename, d)) verbose("... FOUND '%s' in '%s'\n" % (filename, d))
result = node result = node
break break
@ -2954,3 +3031,45 @@ class FileFinder:
return result return result
find_file = FileFinder().find_file find_file = FileFinder().find_file
def invalidate_node_memos(targets):
"""
Invalidate the memoized values of all Nodes (files or directories)
that are associated with the given entries. Has been added to
clear the cache of nodes affected by a direct execution of an
action (e.g. Delete/Copy/Chmod). Existing Node caches become
inconsistent if the action is run through Execute(). The argument
`targets` can be a single Node object or filename, or a sequence
of Nodes/filenames.
"""
from traceback import extract_stack
# First check if the cache really needs to be flushed. Only
# actions run in the SConscript with Execute() seem to be
# affected. XXX The way to check if Execute() is in the stacktrace
# is a very dirty hack and should be replaced by a more sensible
# solution.
for f in extract_stack():
if f[2] == 'Execute' and f[0][-14:] == 'Environment.py':
break
else:
# Dont have to invalidate, so return
return
if not SCons.Util.is_List(targets):
targets = [targets]
for entry in targets:
# If the target is a Node object, clear the cache. If it is a
# filename, look up potentially existing Node object first.
try:
entry.clear_memoized_values()
except AttributeError:
# Not a Node object, try to look up Node by filename. XXX
# This creates Node objects even for those filenames which
# do not correspond to an existing Node object.
node = get_default_fs().Entry(entry)
if node:
node.clear_memoized_values()

View file

@ -5,7 +5,7 @@ Python nodes.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ Python nodes.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Node/Python.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Node/Python.py 3842 2008/12/20 22:59:52 scons"
import SCons.Node import SCons.Node
@ -56,9 +56,12 @@ class Value(SCons.Node.Node):
if not built_value is None: if not built_value is None:
self.built_value = built_value self.built_value = built_value
def __str__(self): def str_for_display(self):
return repr(self.value) return repr(self.value)
def __str__(self):
return str(self.value)
def make_ready(self): def make_ready(self):
self.get_csig() self.get_csig()

View file

@ -20,7 +20,7 @@ be able to depend on any other type of "thing."
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -42,11 +42,10 @@ be able to depend on any other type of "thing."
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Node/__init__.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Node/__init__.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import copy import copy
from itertools import chain, izip
import string import string
import UserList import UserList
@ -75,7 +74,7 @@ executed = 4
failed = 5 failed = 5
StateString = { StateString = {
0 : "0", 0 : "no_state",
1 : "pending", 1 : "pending",
2 : "executing", 2 : "executing",
3 : "up_to_date", 3 : "up_to_date",
@ -202,15 +201,16 @@ class Node:
# a class. (Of course, we could always still do that in the # a class. (Of course, we could always still do that in the
# future if we had a good reason to...). # future if we had a good reason to...).
self.sources = [] # source files used to build node self.sources = [] # source files used to build node
self.sources_dict = {} self.sources_set = set()
self._specific_sources = False
self.depends = [] # explicit dependencies (from Depends) self.depends = [] # explicit dependencies (from Depends)
self.depends_dict = {} self.depends_set = set()
self.ignore = [] # dependencies to ignore self.ignore = [] # dependencies to ignore
self.ignore_dict = {} self.ignore_set = set()
self.prerequisites = SCons.Util.UniqueList() self.prerequisites = SCons.Util.UniqueList()
self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
self.waiting_parents = {} self.waiting_parents = set()
self.waiting_s_e = {} self.waiting_s_e = set()
self.ref_count = 0 self.ref_count = 0
self.wkids = None # Kids yet to walk, when it's an array self.wkids = None # Kids yet to walk, when it's an array
@ -220,12 +220,11 @@ class Node:
self.noclean = 0 self.noclean = 0
self.nocache = 0 self.nocache = 0
self.always_build = None self.always_build = None
self.found_includes = {}
self.includes = None self.includes = None
self.attributes = self.Attrs() # Generic place to stick information about the Node. 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_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target self.side_effects = [] # the side effects of building this target
self.linked = 0 # is this node linked to the build directory? self.linked = 0 # is this node linked to the variant directory?
self.clear_memoized_values() self.clear_memoized_values()
@ -330,24 +329,29 @@ class Node:
is out-of-date and must be rebuilt, but before actually calling is out-of-date and must be rebuilt, but before actually calling
the method to build the Node. the method to build the Node.
This default implemenation checks that all children either exist This default implementation checks that explicit or implicit
or are derived, and initializes the BuildInfo structure that dependencies either exist or are derived, and initializes the
will hold the information about how this node is, uh, built. BuildInfo structure that will hold the information about how
this node is, uh, built.
(The existence of source files is checked separately by the
Executor, which aggregates checks for all of the targets built
by a specific action.)
Overriding this method allows for for a Node subclass to remove Overriding this method allows for for a Node subclass to remove
the underlying file from the file system. Note that subclass the underlying file from the file system. Note that subclass
methods should call this base class method to get the child methods should call this base class method to get the child
check and the BuildInfo structure. check and the BuildInfo structure.
""" """
l = self.depends for d in self.depends:
if d.missing():
msg = "Explicit dependency `%s' not found, needed by target `%s'."
raise SCons.Errors.StopError, msg % (d, self)
if not self.implicit is None: if not self.implicit is None:
l = l + self.implicit for i in self.implicit:
missing_sources = self.get_executor().get_missing_sources() \ if i.missing():
+ filter(lambda c: c.missing(), l) msg = "Implicit dependency `%s' not found, needed by target `%s'."
if missing_sources: raise SCons.Errors.StopError, msg % (i, self)
desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self)
raise SCons.Errors.StopError, desc
self.binfo = self.get_binfo() self.binfo = self.get_binfo()
def build(self, **kw): def build(self, **kw):
@ -373,9 +377,8 @@ class Node:
# Clear the implicit dependency caches of any Nodes # Clear the implicit dependency caches of any Nodes
# waiting for this Node to be built. # waiting for this Node to be built.
for parent in self.waiting_parents.keys(): for parent in self.waiting_parents:
parent.implicit = None parent.implicit = None
parent.del_binfo()
self.clear() self.clear()
@ -399,7 +402,7 @@ class Node:
# #
def add_to_waiting_s_e(self, node): def add_to_waiting_s_e(self, node):
self.waiting_s_e[node] = 1 self.waiting_s_e.add(node)
def add_to_waiting_parents(self, node): def add_to_waiting_parents(self, node):
""" """
@ -410,37 +413,26 @@ class Node:
True and False instead...) True and False instead...)
""" """
wp = self.waiting_parents wp = self.waiting_parents
if wp.has_key(node): if node in wp:
result = 0 return 0
else: wp.add(node)
result = 1 return 1
wp[node] = 1
return result
def call_for_all_waiting_parents(self, func):
func(self)
for parent in self.waiting_parents.keys():
parent.call_for_all_waiting_parents(func)
def postprocess(self): def postprocess(self):
"""Clean up anything we don't need to hang onto after we've """Clean up anything we don't need to hang onto after we've
been built.""" been built."""
self.executor_cleanup() self.executor_cleanup()
self.waiting_parents = {} self.waiting_parents = set()
def clear(self): def clear(self):
"""Completely clear a Node of all its cached state (so that it """Completely clear a Node of all its cached state (so that it
can be re-evaluated by interfaces that do continuous integration can be re-evaluated by interfaces that do continuous integration
builds). builds).
""" """
# Note in case it's important in the future: We also used to clear # The del_binfo() call here isn't necessary for normal execution,
# the build information (the lists of dependencies) here like this: # but is for interactive mode, where we might rebuild the same
# # target and need to start from scratch.
# self.del_binfo() self.del_binfo()
#
# But we now rely on the fact that we're going to look at that
# once before the build, and then store the results in the
# .sconsign file after the build.
self.clear_memoized_values() self.clear_memoized_values()
self.ninfo = self.new_ninfo() self.ninfo = self.new_ninfo()
self.executor_cleanup() self.executor_cleanup()
@ -449,7 +441,6 @@ class Node:
except AttributeError: except AttributeError:
pass pass
self.includes = None self.includes = None
self.found_includes = {}
def clear_memoized_values(self): def clear_memoized_values(self):
self._memo = {} self._memo = {}
@ -510,7 +501,7 @@ class Node:
Returns true iff this node is derived (i.e. built). Returns true iff this node is derived (i.e. built).
This should return true only for nodes whose path should be in This should return true only for nodes whose path should be in
the build directory when duplicate=0 and should contribute their build the variant directory when duplicate=0 and should contribute their build
signatures when they are used as source files to other derived files. For signatures when they are used as source files to other derived files. For
example: source with source builders are not derived in this sense, example: source with source builders are not derived in this sense,
and hence should not return true. and hence should not return true.
@ -597,9 +588,9 @@ class Node:
def add_to_implicit(self, deps): def add_to_implicit(self, deps):
if not hasattr(self, 'implicit') or self.implicit is None: if not hasattr(self, 'implicit') or self.implicit is None:
self.implicit = [] self.implicit = []
self.implicit_dict = {} self.implicit_set = set()
self._children_reset() self._children_reset()
self._add_child(self.implicit, self.implicit_dict, deps) self._add_child(self.implicit, self.implicit_set, deps)
def scan(self): def scan(self):
"""Scan this node's dependents for implicit dependencies.""" """Scan this node's dependents for implicit dependencies."""
@ -609,7 +600,7 @@ class Node:
if not self.implicit is None: if not self.implicit is None:
return return
self.implicit = [] self.implicit = []
self.implicit_dict = {} self.implicit_set = set()
self._children_reset() self._children_reset()
if not self.has_builder(): if not self.has_builder():
return return
@ -638,9 +629,7 @@ class Node:
# one of this node's sources has changed, # one of this node's sources has changed,
# so we must recalculate the implicit deps: # so we must recalculate the implicit deps:
self.implicit = [] self.implicit = []
self.implicit_dict = {} self.implicit_set = set()
self._children_reset()
self.del_binfo()
# Have the executor scan the sources. # Have the executor scan the sources.
executor.scan_sources(self.builder.source_scanner) executor.scan_sources(self.builder.source_scanner)
@ -713,33 +702,44 @@ class Node:
self.binfo = binfo self.binfo = binfo
executor = self.get_executor() executor = self.get_executor()
ignore_set = self.ignore_set
sources = executor.get_unignored_sources(self.ignore)
depends = self.depends
implicit = self.implicit or []
if self.ignore:
depends = filter(self.do_not_ignore, depends)
implicit = filter(self.do_not_ignore, implicit)
def get_ninfo(node):
return node.get_ninfo()
sourcesigs = map(get_ninfo, sources)
dependsigs = map(get_ninfo, depends)
implicitsigs = map(get_ninfo, implicit)
if self.has_builder(): if self.has_builder():
binfo.bact = str(executor) binfo.bact = str(executor)
binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) binfo.bactsig = SCons.Util.MD5signature(executor.get_contents())
binfo.bsources = sources if self._specific_sources:
binfo.bdepends = depends sources = []
binfo.bimplicit = implicit for s in self.sources:
if s not in ignore_set:
sources.append(s)
else:
sources = executor.get_unignored_sources(self.ignore)
seen = set()
bsources = []
bsourcesigs = []
for s in sources:
if not s in seen:
seen.add(s)
bsources.append(s)
bsourcesigs.append(s.get_ninfo())
binfo.bsources = bsources
binfo.bsourcesigs = bsourcesigs
binfo.bsourcesigs = sourcesigs depends = self.depends
dependsigs = []
for d in depends:
if d not in ignore_set:
dependsigs.append(d.get_ninfo())
binfo.bdepends = depends
binfo.bdependsigs = dependsigs binfo.bdependsigs = dependsigs
implicit = self.implicit or []
implicitsigs = []
for i in implicit:
if i not in ignore_set:
implicitsigs.append(i.get_ninfo())
binfo.bimplicit = implicit
binfo.bimplicitsigs = implicitsigs binfo.bimplicitsigs = implicitsigs
return binfo return binfo
@ -823,7 +823,7 @@ class Node:
def add_dependency(self, depend): def add_dependency(self, depend):
"""Adds dependencies.""" """Adds dependencies."""
try: try:
self._add_child(self.depends, self.depends_dict, depend) self._add_child(self.depends, self.depends_set, depend)
except TypeError, e: except TypeError, e:
e = e.args[0] e = e.args[0]
if SCons.Util.is_List(e): if SCons.Util.is_List(e):
@ -840,7 +840,7 @@ class Node:
def add_ignore(self, depend): def add_ignore(self, depend):
"""Adds dependencies to ignore.""" """Adds dependencies to ignore."""
try: try:
self._add_child(self.ignore, self.ignore_dict, depend) self._add_child(self.ignore, self.ignore_set, depend)
except TypeError, e: except TypeError, e:
e = e.args[0] e = e.args[0]
if SCons.Util.is_List(e): if SCons.Util.is_List(e):
@ -851,8 +851,10 @@ class Node:
def add_source(self, source): def add_source(self, source):
"""Adds sources.""" """Adds sources."""
if self._specific_sources:
return
try: try:
self._add_child(self.sources, self.sources_dict, source) self._add_child(self.sources, self.sources_set, source)
except TypeError, e: except TypeError, e:
e = e.args[0] e = e.args[0]
if SCons.Util.is_List(e): if SCons.Util.is_List(e):
@ -861,9 +863,9 @@ class Node:
s = str(e) s = str(e)
raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
def _add_child(self, collection, dict, child): def _add_child(self, collection, set, child):
"""Adds 'child' to 'collection', first checking 'dict' to see """Adds 'child' to 'collection', first checking 'set' to see if it's
if it's already present.""" already present."""
#if type(child) is not type([]): #if type(child) is not type([]):
# child = [child] # child = [child]
#for c in child: #for c in child:
@ -871,13 +873,17 @@ class Node:
# raise TypeError, c # raise TypeError, c
added = None added = None
for c in child: for c in child:
if not dict.has_key(c): if c not in set:
set.add(c)
collection.append(c) collection.append(c)
dict[c] = 1
added = 1 added = 1
if added: if added:
self._children_reset() self._children_reset()
def set_specific_source(self, source):
self.add_source(source)
self._specific_sources = True
def add_wkid(self, wkid): def add_wkid(self, wkid):
"""Add a node to the list of kids waiting to be evaluated""" """Add a node to the list of kids waiting to be evaluated"""
if self.wkids != None: if self.wkids != None:
@ -889,10 +895,55 @@ class Node:
# build info that it's cached so we can re-calculate it. # build info that it's cached so we can re-calculate it.
self.executor_cleanup() self.executor_cleanup()
def do_not_ignore(self, node): memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
return node not in self.ignore
def _children_get(self):
try:
return self._memo['children_get']
except KeyError:
pass
# The return list may contain duplicate Nodes, especially in
# source trees where there are a lot of repeated #includes
# of a tangle of .h files. Profiling shows, however, that
# eliminating the duplicates with a brute-force approach that
# preserves the order (that is, something like:
#
# u = []
# for n in list:
# if n not in u:
# u.append(n)"
#
# takes more cycles than just letting the underlying methods
# hand back cached values if a Node's information is requested
# multiple times. (Other methods of removing duplicates, like
# using dictionary keys, lose the order, and the only ordered
# dictionary patterns I found all ended up using "not in"
# internally anyway...)
if self.ignore_set:
if self.implicit is None:
iter = chain(self.sources,self.depends)
else:
iter = chain(self.sources, self.depends, self.implicit)
children = []
for i in iter:
if i not in self.ignore_set:
children.append(i)
else:
if self.implicit is None:
children = self.sources + self.depends
else:
children = self.sources + self.depends + self.implicit
self._memo['children_get'] = children
return children
def all_children(self, scan=1):
"""Return a list of all the node's direct children."""
if scan:
self.scan()
def _all_children_get(self):
# The return list may contain duplicate Nodes, especially in # The return list may contain duplicate Nodes, especially in
# source trees where there are a lot of repeated #includes # source trees where there are a lot of repeated #includes
# of a tangle of .h files. Profiling shows, however, that # of a tangle of .h files. Profiling shows, however, that
@ -915,25 +966,6 @@ class Node:
else: else:
return self.sources + self.depends + self.implicit return self.sources + self.depends + self.implicit
memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
def _children_get(self):
try:
return self._memo['children_get']
except KeyError:
pass
children = self._all_children_get()
if self.ignore:
children = filter(self.do_not_ignore, children)
self._memo['children_get'] = children
return children
def all_children(self, scan=1):
"""Return a list of all the node's direct children."""
if scan:
self.scan()
return self._all_children_get()
def children(self, scan=1): def children(self, scan=1):
"""Return a list of the node's direct children, minus those """Return a list of the node's direct children, minus those
that are ignored by this node.""" that are ignored by this node."""
@ -1013,9 +1045,10 @@ class Node:
# entries to equal the new dependency list, for the benefit # entries to equal the new dependency list, for the benefit
# of the loop below that updates node information. # of the loop below that updates node information.
then.extend([None] * diff) then.extend([None] * diff)
if t: Trace(': old %s new %s' % (len(then), len(children)))
result = True result = True
for child, prev_ni in zip(children, then): for child, prev_ni in izip(children, then):
if child.changed_since_last_build(self, prev_ni): if child.changed_since_last_build(self, prev_ni):
if t: Trace(': %s changed' % child) if t: Trace(': %s changed' % child)
result = True result = True
@ -1071,7 +1104,10 @@ class Node:
env = self.get_build_env() env = self.get_build_env()
for s in self.sources: for s in self.sources:
scanner = self.get_source_scanner(s) scanner = self.get_source_scanner(s)
if scanner:
path = self.get_build_scanner_path(scanner) path = self.get_build_scanner_path(scanner)
else:
path = None
def f(node, env=env, scanner=scanner, path=path): def f(node, env=env, scanner=scanner, path=path):
return node.get_found_includes(env, scanner, path) return node.get_found_includes(env, scanner, path)
return SCons.Util.render_tree(s, f, 1) return SCons.Util.render_tree(s, f, 1)
@ -1158,8 +1194,8 @@ class Node:
new_bkids = new.bsources + new.bdepends + new.bimplicit new_bkids = new.bsources + new.bdepends + new.bimplicit
new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
osig = dict(zip(old_bkids, old_bkidsigs)) osig = dict(izip(old_bkids, old_bkidsigs))
nsig = dict(zip(new_bkids, new_bkidsigs)) nsig = dict(izip(new_bkids, new_bkidsigs))
# The sources and dependencies we'll want to report are all stored # The sources and dependencies we'll want to report are all stored
# as relative paths to this target's directory, but we want to # as relative paths to this target's directory, but we want to
@ -1167,7 +1203,10 @@ class Node:
# so we only print them after running them through this lambda # so we only print them after running them through this lambda
# to turn them into the right relative Node and then return # to turn them into the right relative Node and then return
# its string. # its string.
stringify = lambda s, E=self.dir.Entry: str(E(s)) def stringify( s, E=self.dir.Entry ) :
if hasattr( s, 'dir' ) :
return str(E(s))
return str(s)
lines = [] lines = []

View file

@ -0,0 +1,44 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/BoolOption.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
warned = False
def BoolOption(*args, **kw):
global warned
if not warned:
msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
return apply(SCons.Variables.BoolVariable, args, kw)

View file

@ -0,0 +1,44 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/EnumOption.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
warned = False
def EnumOption(*args, **kw):
global warned
if not warned:
msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
return apply(SCons.Variables.EnumVariable, args, kw)

View file

@ -0,0 +1,44 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/ListOption.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
warned = False
def ListOption(*args, **kw):
global warned
if not warned:
msg = "The ListOption() function is deprecated; use the ListVariable() function instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
return apply(SCons.Variables.ListVariable, args, kw)

View file

@ -0,0 +1,44 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/PackageOption.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
warned = False
def PackageOption(*args, **kw):
global warned
if not warned:
msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
return apply(SCons.Variables.PackageVariable, args, kw)

View file

@ -0,0 +1,70 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/PathOption.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
warned = False
class _PathOptionClass:
def warn(self):
global warned
if not warned:
msg = "The PathOption() function is deprecated; use the PathVariable() function instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
def __call__(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable, args, kw)
def PathAccept(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable.PathAccept, args, kw)
def PathIsDir(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable.PathIsDir, args, kw)
def PathIsDirCreate(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable.PathIsDirCreate, args, kw)
def PathIsFile(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable.PathIsFile, args, kw)
def PathExists(self, *args, **kw):
self.warn()
return apply(SCons.Variables.PathVariable.PathExists, args, kw)
PathOption = _PathOptionClass()

View file

@ -0,0 +1,68 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Options/__init__.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Options module hierarchy
This is for backwards compatibility. The new equivalent is the Variables/
class hierarchy. These will have deprecation warnings added (some day),
and will then be removed entirely (some day).
"""
import SCons.Variables
import SCons.Warnings
from BoolOption import BoolOption # okay
from EnumOption import EnumOption # okay
from ListOption import ListOption # naja
from PackageOption import PackageOption # naja
from PathOption import PathOption # okay
warned = False
class Options(SCons.Variables.Variables):
def __init__(self, *args, **kw):
global warned
if not warned:
msg = "The Options class is deprecated; use the Variables class instead."
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
warned = True
apply(SCons.Variables.Variables.__init__,
(self,) + args,
kw)
def AddOptions(self, *args, **kw):
return apply(SCons.Variables.Variables.AddVariables,
(self,) + args,
kw)
def UnknownOptions(self, *args, **kw):
return apply(SCons.Variables.Variables.UnknownVariables,
(self,) + args,
kw)
def FormatOptionHelpText(self, *args, **kw):
return apply(SCons.Variables.Variables.FormatVariableHelpText,
(self,) + args,
kw)

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/PathList.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/PathList.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """SCons.PathList __doc__ = """SCons.PathList
@ -59,7 +59,7 @@ def node_conv(obj):
try: try:
get = obj.get get = obj.get
except AttributeError: except AttributeError:
if isinstance(obj, SCons.Node.Node): if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ):
result = obj result = obj
else: else:
result = str(obj) result = str(obj)
@ -132,10 +132,9 @@ class _PathList:
value = env.subst(value, target=target, source=source, value = env.subst(value, target=target, source=source,
conv=node_conv) conv=node_conv)
if SCons.Util.is_Sequence(value): if SCons.Util.is_Sequence(value):
# It came back as a string or tuple, which in this result.extend(value)
# case usually means some variable expanded to an continue
# actually Dir node. Concatenate the values.
value = string.join(map(str, value), '')
elif type == TYPE_OBJECT: elif type == TYPE_OBJECT:
value = node_conv(value) value = node_conv(value)
if value: if value:

View file

@ -20,7 +20,7 @@ their own platform definition.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -42,7 +42,7 @@ their own platform definition.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/__init__.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/__init__.py 3842 2008/12/20 22:59:52 scons"
import imp import imp
import os import os

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/aix.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/aix.py 3842 2008/12/20 22:59:52 scons"
import os import os
import string import string

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/cygwin.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/cygwin.py 3842 2008/12/20 22:59:52 scons"
import posix import posix
from SCons.Platform import TempFileMunge from SCons.Platform import TempFileMunge

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/darwin.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/darwin.py 3842 2008/12/20 22:59:52 scons"
import posix import posix

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/hpux.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/hpux.py 3842 2008/12/20 22:59:52 scons"
import posix import posix

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/irix.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/irix.py 3842 2008/12/20 22:59:52 scons"
import posix import posix

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/os2.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/os2.py 3842 2008/12/20 22:59:52 scons"
def generate(env): def generate(env):
if not env.has_key('ENV'): if not env.has_key('ENV'):

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,12 +30,13 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/posix.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/posix.py 3842 2008/12/20 22:59:52 scons"
import errno
import os import os
import os.path import os.path
import popen2
import string import string
import subprocess
import sys import sys
import select import select
@ -50,7 +51,7 @@ exitvalmap = {
def escape(arg): def escape(arg):
"escape shell special characters" "escape shell special characters"
slash = '\\' slash = '\\'
special = '"$' special = '"$()'
arg = string.replace(arg, slash, slash+slash) arg = string.replace(arg, slash, slash+slash)
for c in special: for c in special:
@ -92,7 +93,7 @@ def _get_env_command(sh, escape, cmd, args, env):
s = string.join(args) s = string.join(args)
if env: if env:
l = ['env', '-'] + \ l = ['env', '-'] + \
map(lambda t, e=escape: t[0]+'='+e(t[1]), env.items()) + \ map(lambda t, e=escape: e(t[0])+'='+e(t[1]), env.items()) + \
[sh, '-c', escape(s)] [sh, '-c', escape(s)]
s = string.join(l) s = string.join(l)
return s return s
@ -109,6 +110,7 @@ def fork_spawn(sh, escape, cmd, args, env):
def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr):
stdout_eof = stderr_eof = 0 stdout_eof = stderr_eof = 0
while not (stdout_eof and stderr_eof): while not (stdout_eof and stderr_eof):
try:
(i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], [])
if cmd_stdout in i: if cmd_stdout in i:
str = cmd_stdout.read() str = cmd_stdout.read()
@ -124,10 +126,15 @@ def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr):
else: else:
#sys.__stderr__.write( "str(stderr) = %s\n" % str ) #sys.__stderr__.write( "str(stderr) = %s\n" % str )
stderr.write(str) stderr.write(str)
except select.error, (_errno, _strerror):
if _errno != errno.EINTR:
raise
def exec_popen3(l, env, stdout, stderr): def exec_popen3(l, env, stdout, stderr):
proc = popen2.Popen3(string.join(l), 1) proc = subprocess.Popen(string.join(l),
process_cmd_output(proc.fromchild, proc.childerr, stdout, stderr) stdout=stdout,
stderr=stderr,
shell=True)
stat = proc.wait() stat = proc.wait()
if stat & 0xff: if stat & 0xff:
return stat | 0x80 return stat | 0x80
@ -235,7 +242,7 @@ def generate(env):
env['LIBSUFFIX'] = '.a' env['LIBSUFFIX'] = '.a'
env['SHLIBPREFIX'] = '$LIBPREFIX' env['SHLIBPREFIX'] = '$LIBPREFIX'
env['SHLIBSUFFIX'] = '.so' env['SHLIBSUFFIX'] = '.so'
env['LIBPREFIXES'] = '$LIBPREFIX' env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
env['PSPAWN'] = pspawn env['PSPAWN'] = pspawn
env['SPAWN'] = spawn env['SPAWN'] = spawn

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/sunos.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/sunos.py 3842 2008/12/20 22:59:52 scons"
import posix import posix

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Platform/win32.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Platform/win32.py 3842 2008/12/20 22:59:52 scons"
import os import os
import os.path import os.path

View file

@ -4,7 +4,7 @@ Autoconf-like configuration support.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -26,9 +26,7 @@ Autoconf-like configuration support.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/SConf.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/SConf.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import os import os
import re import re
@ -235,7 +233,9 @@ class SConfBuildTask(SCons.Taskmaster.Task):
raise raise
elif issubclass(exc_type, SCons.Errors.BuildError): elif issubclass(exc_type, SCons.Errors.BuildError):
# we ignore Build Errors (occurs, when a test doesn't pass) # we ignore Build Errors (occurs, when a test doesn't pass)
pass # Clear the exception to prevent the contained traceback
# to build a reference cycle.
self.exc_clear()
else: else:
self.display('Caught exception while building "%s":\n' % self.display('Caught exception while building "%s":\n' %
self.targets[0]) self.targets[0])
@ -381,7 +381,7 @@ class SConfBase:
e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
defines a custom test. defines a custom test.
Note also the conf_dir and log_file arguments (you may want to Note also the conf_dir and log_file arguments (you may want to
build tests in the BuildDir, not in the SourceDir) build tests in the VariantDir, not in the SourceDir)
""" """
global SConfFS global SConfFS
if not SConfFS: if not SConfFS:
@ -401,14 +401,19 @@ class SConfBase:
# add default tests # add default tests
default_tests = { default_tests = {
'CheckCC' : CheckCC,
'CheckCXX' : CheckCXX,
'CheckSHCC' : CheckSHCC,
'CheckSHCXX' : CheckSHCXX,
'CheckFunc' : CheckFunc, 'CheckFunc' : CheckFunc,
'CheckType' : CheckType, 'CheckType' : CheckType,
'CheckTypeSize' : CheckTypeSize, 'CheckTypeSize' : CheckTypeSize,
'CheckDeclaration' : CheckDeclaration,
'CheckHeader' : CheckHeader, 'CheckHeader' : CheckHeader,
'CheckCHeader' : CheckCHeader, 'CheckCHeader' : CheckCHeader,
'CheckCXXHeader' : CheckCXXHeader, 'CheckCXXHeader' : CheckCXXHeader,
'CheckLib' : CheckLib, 'CheckLib' : CheckLib,
'CheckLibWithHeader' : CheckLibWithHeader 'CheckLibWithHeader' : CheckLibWithHeader,
} }
self.AddTests(default_tests) self.AddTests(default_tests)
self.AddTests(custom_tests) self.AddTests(custom_tests)
@ -425,6 +430,31 @@ class SConfBase:
self._shutdown() self._shutdown()
return self.env return self.env
def Define(self, name, value = None, comment = None):
"""
Define a pre processor symbol name, with the optional given value in the
current config header.
If value is None (default), then #define name is written. If value is not
none, then #define name value is written.
comment is a string which will be put as a C comment in the
header, to explain the meaning of the value (appropriate C comments /* and
*/ will be put automatically."""
lines = []
if comment:
comment_str = "/* %s */" % comment
lines.append(comment_str)
if value is not None:
define_str = "#define %s %s" % (name, value)
else:
define_str = "#define %s" % name
lines.append(define_str)
lines.append('')
self.config_h_text = self.config_h_text + string.join(lines, '\n')
def BuildNodes(self, nodes): def BuildNodes(self, nodes):
""" """
Tries to build the given nodes immediately. Returns 1 on success, Tries to build the given nodes immediately. Returns 1 on success,
@ -797,6 +827,17 @@ class CheckContext:
# TODO: should use self.vardict for $CC, $CPPFLAGS, etc. # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
return not self.TryBuild(self.env.Object, text, ext) return not self.TryBuild(self.env.Object, text, ext)
def CompileSharedObject(self, text, ext):
self.sconf.cached = 1
# TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
return not self.TryBuild(self.env.SharedObject, text, ext)
def RunProg(self, text, ext):
self.sconf.cached = 1
# TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
st, out = self.TryRun(text, ext)
return not st, out
def AppendLIBS(self, lib_name_list): def AppendLIBS(self, lib_name_list):
oldLIBS = self.env.get( 'LIBS', [] ) oldLIBS = self.env.get( 'LIBS', [] )
self.env.Append(LIBS = lib_name_list) self.env.Append(LIBS = lib_name_list)
@ -855,6 +896,13 @@ def CheckTypeSize(context, type_name, includes = "", language = None, expect = N
context.did_show_result = 1 context.did_show_result = 1
return res return res
def CheckDeclaration(context, declaration, includes = "", language = None):
res = SCons.Conftest.CheckDeclaration(context, declaration,
includes = includes,
language = language)
context.did_show_result = 1
return not res
def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
# used by CheckHeader and CheckLibWithHeader to produce C - #include # used by CheckHeader and CheckLibWithHeader to produce C - #include
# statements from the specified header (list) # statements from the specified header (list)
@ -883,6 +931,22 @@ def CheckHeader(context, header, include_quotes = '<>', language = None):
context.did_show_result = 1 context.did_show_result = 1
return not res return not res
def CheckCC(context):
res = SCons.Conftest.CheckCC(context)
return not res
def CheckCXX(context):
res = SCons.Conftest.CheckCXX(context)
return not res
def CheckSHCC(context):
res = SCons.Conftest.CheckSHCC(context)
return not res
def CheckSHCXX(context):
res = SCons.Conftest.CheckSHCXX(context)
return not res
# Bram: Make this function obsolete? CheckHeader() is more generic. # Bram: Make this function obsolete? CheckHeader() is more generic.
def CheckCHeader(context, header, include_quotes = '""'): def CheckCHeader(context, header, include_quotes = '""'):

View file

@ -5,7 +5,7 @@ Writing and reading information to the .sconsign file or files.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,9 +27,7 @@ Writing and reading information to the .sconsign file or files.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/SConsign.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/SConsign.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import cPickle import cPickle
import os import os

View file

@ -0,0 +1,126 @@
"""SCons.Scanner.C
This module implements the depenency scanner for C/C++ code.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Scanner/C.py 3842 2008/12/20 22:59:52 scons"
import SCons.Node.FS
import SCons.Scanner
import SCons.Util
import SCons.cpp
class SConsCPPScanner(SCons.cpp.PreProcessor):
"""
SCons-specific subclass of the cpp.py module's processing.
We subclass this so that: 1) we can deal with files represented
by Nodes, not strings; 2) we can keep track of the files that are
missing.
"""
def __init__(self, *args, **kw):
apply(SCons.cpp.PreProcessor.__init__, (self,)+args, kw)
self.missing = []
def initialize_result(self, fname):
self.result = SCons.Util.UniqueList([fname])
def finalize_result(self, fname):
return self.result[1:]
def find_include_file(self, t):
keyword, quote, fname = t
result = SCons.Node.FS.find_file(fname, self.searchpath[quote])
if not result:
self.missing.append((fname, self.current_file))
return result
def read_file(self, file):
try:
fp = open(str(file.rfile()))
except EnvironmentError, e:
self.missing.append((file, self.current_file))
return ''
else:
return fp.read()
def dictify_CPPDEFINES(env):
cppdefines = env.get('CPPDEFINES', {})
if cppdefines is None:
return {}
if SCons.Util.is_Sequence(cppdefines):
result = {}
for c in cppdefines:
if SCons.Util.is_Sequence(c):
result[c[0]] = c[1]
else:
result[c] = None
return result
if not SCons.Util.is_Dict(cppdefines):
return {cppdefines : None}
return cppdefines
class SConsCPPScannerWrapper:
"""
The SCons wrapper around a cpp.py scanner.
This is the actual glue between the calling conventions of generic
SCons scanners, and the (subclass of) cpp.py class that knows how
to look for #include lines with reasonably real C-preprocessor-like
evaluation of #if/#ifdef/#else/#elif lines.
"""
def __init__(self, name, variable):
self.name = name
self.path = SCons.Scanner.FindPathDirs(variable)
def __call__(self, node, env, path = ()):
cpp = SConsCPPScanner(current = node.get_dir(),
cpppath = path,
dict = dictify_CPPDEFINES(env))
result = cpp(node)
for included, includer in cpp.missing:
fmt = "No dependency generated for file: %s (included from: %s) -- file not found"
SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
fmt % (included, includer))
return result
def recurse_nodes(self, nodes):
return nodes
def select(self, node):
return self
def CScanner():
"""Return a prototype Scanner instance for scanning source files
that use the C pre-processor"""
# Here's how we would (or might) use the CPP scanner code above that
# knows how to evaluate #if/#ifdef/#else/#elif lines when searching
# for #includes. This is commented out for now until we add the
# right configurability to let users pick between the scanners.
#return SConsCPPScannerWrapper("CScanner", "CPPPATH")
cs = SCons.Scanner.ClassicCPP("CScanner",
"$CPPSUFFIXES",
"CPPPATH",
'^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")')
return cs

View file

@ -8,7 +8,7 @@ Coded by Andy Friesen
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,24 +30,39 @@ Coded by Andy Friesen
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/D.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/D.py 3842 2008/12/20 22:59:52 scons"
import re
import string import string
import SCons.Scanner import SCons.Scanner
def DScanner(): def DScanner():
"""Return a prototype Scanner instance for scanning D source files""" """Return a prototype Scanner instance for scanning D source files"""
ds = D(name = "DScanner", ds = D()
suffixes = '$DSUFFIXES',
path_variable = 'DPATH',
regex = 'import\s+([^\;]*)\;')
return ds return ds
class D(SCons.Scanner.Classic): class D(SCons.Scanner.Classic):
def __init__ (self):
SCons.Scanner.Classic.__init__ (self,
name = "DScanner",
suffixes = '$DSUFFIXES',
path_variable = 'DPATH',
regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;')
self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M)
def find_include(self, include, source_dir, path): def find_include(self, include, source_dir, path):
# translate dots (package separators) to slashes # translate dots (package separators) to slashes
inc = string.replace(include, '.', '/') inc = string.replace(include, '.', '/')
i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path)
if i is None:
i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path)
return i, include return i, include
def find_include_names(self, node):
includes = []
for i in self.cre.findall(node.get_contents()):
includes = includes + self.cre2.findall(i)
return includes

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/Dir.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/Dir.py 3842 2008/12/20 22:59:52 scons"
import SCons.Node.FS import SCons.Node.FS
import SCons.Scanner import SCons.Scanner

View file

@ -5,7 +5,7 @@ This module implements the dependency scanner for Fortran code.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ This module implements the dependency scanner for Fortran code.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/Fortran.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/Fortran.py 3842 2008/12/20 22:59:52 scons"
import re import re
import string import string

View file

@ -6,7 +6,7 @@ Definition Language) files.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -28,7 +28,7 @@ Definition Language) files.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/IDL.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/IDL.py 3842 2008/12/20 22:59:52 scons"
import SCons.Node.FS import SCons.Node.FS
import SCons.Scanner import SCons.Scanner

View file

@ -0,0 +1,334 @@
"""SCons.Scanner.LaTeX
This module implements the dependency scanner for LaTeX code.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Scanner/LaTeX.py 3842 2008/12/20 22:59:52 scons"
import os.path
import string
import re
import SCons.Scanner
import SCons.Util
# list of graphics file extensions for TeX and LaTeX
TexGraphics = ['.eps', '.ps']
LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif']
# Used as a return value of modify_env_var if the variable is not set.
class _Null:
pass
_null = _Null
# The user specifies the paths in env[variable], similar to other builders.
# They may be relative and must be converted to absolute, as expected
# by LaTeX and Co. The environment may already have some paths in
# env['ENV'][var]. These paths are honored, but the env[var] paths have
# higher precedence. All changes are un-done on exit.
def modify_env_var(env, var, abspath):
try:
save = env['ENV'][var]
except KeyError:
save = _null
env.PrependENVPath(var, abspath)
try:
if SCons.Util.is_List(env[var]):
#TODO(1.5)
#env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]])
env.PrependENVPath(var, map(lambda p: os.path.abspath(str(p)), env[var]))
else:
# Split at os.pathsep to convert into absolute path
#TODO(1.5) env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)])
env.PrependENVPath(var, map(lambda p: os.path.abspath(p), string.split(str(env[var]), os.pathsep)))
except KeyError:
pass
# Convert into a string explicitly to append ":" (without which it won't search system
# paths as well). The problem is that env.AppendENVPath(var, ":")
# does not work, refuses to append ":" (os.pathsep).
if SCons.Util.is_List(env['ENV'][var]):
# TODO(1.5)
#env['ENV'][var] = os.pathsep.join(env['ENV'][var])
env['ENV'][var] = string.join(env['ENV'][var], os.pathsep)
# Append the trailing os.pathsep character here to catch the case with no env[var]
env['ENV'][var] = env['ENV'][var] + os.pathsep
return save
class FindENVPathDirs:
"""A class to bind a specific *PATH variable name to a function that
will return all of the *path directories."""
def __init__(self, variable):
self.variable = variable
def __call__(self, env, dir=None, target=None, source=None, argument=None):
import SCons.PathList
try:
path = env['ENV'][self.variable]
except KeyError:
return ()
dir = dir or env.fs._cwd
path = SCons.PathList.PathList(path).subst_path(env, target, source)
return tuple(dir.Rfindalldirs(path))
def LaTeXScanner():
"""Return a prototype Scanner instance for scanning LaTeX source files
when built with latex.
"""
ds = LaTeX(name = "LaTeXScanner",
suffixes = '$LATEXSUFFIXES',
# in the search order, see below in LaTeX class docstring
graphics_extensions = TexGraphics,
recursive = 0)
return ds
def PDFLaTeXScanner():
"""Return a prototype Scanner instance for scanning LaTeX source files
when built with pdflatex.
"""
ds = LaTeX(name = "PDFLaTeXScanner",
suffixes = '$LATEXSUFFIXES',
# in the search order, see below in LaTeX class docstring
graphics_extensions = LatexGraphics,
recursive = 0)
return ds
class LaTeX(SCons.Scanner.Base):
"""Class for scanning LaTeX files for included files.
Unlike most scanners, which use regular expressions that just
return the included file name, this returns a tuple consisting
of the keyword for the inclusion ("include", "includegraphics",
"input", or "bibliography"), and then the file name itself.
Based on a quick look at LaTeX documentation, it seems that we
should append .tex suffix for the "include" keywords, append .tex if
there is no extension for the "input" keyword, and need to add .bib
for the "bibliography" keyword that does not accept extensions by itself.
Finally, if there is no extension for an "includegraphics" keyword
latex will append .ps or .eps to find the file, while pdftex may use .pdf,
.jpg, .tif, .mps, or .png.
The actual subset and search order may be altered by
DeclareGraphicsExtensions command. This complication is ignored.
The default order corresponds to experimentation with teTeX
$ latex --version
pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4)
kpathsea version 3.5.4
The order is:
['.eps', '.ps'] for latex
['.png', '.pdf', '.jpg', '.tif'].
Another difference is that the search path is determined by the type
of the file being searched:
env['TEXINPUTS'] for "input" and "include" keywords
env['TEXINPUTS'] for "includegraphics" keyword
env['BIBINPUTS'] for "bibliography" keyword
env['BSTINPUTS'] for "bibliographystyle" keyword
FIXME: also look for the class or style in document[class|style]{}
FIXME: also look for the argument of bibliographystyle{}
"""
keyword_paths = {'include': 'TEXINPUTS',
'input': 'TEXINPUTS',
'includegraphics': 'TEXINPUTS',
'bibliography': 'BIBINPUTS',
'bibliographystyle': 'BSTINPUTS',
'usepackage': 'TEXINPUTS'}
env_variables = SCons.Util.unique(keyword_paths.values())
def __init__(self, name, suffixes, graphics_extensions, *args, **kw):
# We have to include \n with the % we exclude from the first part
# part of the regex because the expression is compiled with re.M.
# Without the \n, the ^ could match the beginning of a *previous*
# line followed by one or more newline characters (i.e. blank
# lines), interfering with a match on the next line.
regex = r'^[^%\n]*\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}'
self.cre = re.compile(regex, re.M)
self.graphics_extensions = graphics_extensions
def _scan(node, env, path=(), self=self):
node = node.rfile()
if not node.exists():
return []
return self.scan(node, path)
class FindMultiPathDirs:
"""The stock FindPathDirs function has the wrong granularity:
it is called once per target, while we need the path that depends
on what kind of included files is being searched. This wrapper
hides multiple instances of FindPathDirs, one per the LaTeX path
variable in the environment. When invoked, the function calculates
and returns all the required paths as a dictionary (converted into
a tuple to become hashable). Then the scan function converts it
back and uses a dictionary of tuples rather than a single tuple
of paths.
"""
def __init__(self, dictionary):
self.dictionary = {}
for k,n in dictionary.items():
self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n),
FindENVPathDirs(n) )
def __call__(self, env, dir=None, target=None, source=None,
argument=None):
di = {}
for k,(c,cENV) in self.dictionary.items():
di[k] = ( c(env, dir=None, target=None, source=None,
argument=None) ,
cENV(env, dir=None, target=None, source=None,
argument=None) )
# To prevent "dict is not hashable error"
return tuple(di.items())
class LaTeXScanCheck:
"""Skip all but LaTeX source files, i.e., do not scan *.eps,
*.pdf, *.jpg, etc.
"""
def __init__(self, suffixes):
self.suffixes = suffixes
def __call__(self, node, env):
current = not node.has_builder() or node.is_up_to_date()
scannable = node.get_suffix() in env.subst_list(self.suffixes)[0]
# Returning false means that the file is not scanned.
return scannable and current
kw['function'] = _scan
kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths)
kw['recursive'] = 1
kw['skeys'] = suffixes
kw['scan_check'] = LaTeXScanCheck(suffixes)
kw['name'] = name
apply(SCons.Scanner.Base.__init__, (self,) + args, kw)
def _latex_names(self, include):
filename = include[1]
if include[0] == 'input':
base, ext = os.path.splitext( filename )
if ext == "":
return [filename + '.tex']
if (include[0] == 'include'):
return [filename + '.tex']
if include[0] == 'bibliography':
base, ext = os.path.splitext( filename )
if ext == "":
return [filename + '.bib']
if include[0] == 'usepackage':
base, ext = os.path.splitext( filename )
if ext == "":
return [filename + '.sty']
if include[0] == 'includegraphics':
base, ext = os.path.splitext( filename )
if ext == "":
#TODO(1.5) return [filename + e for e in self.graphics_extensions]
return map(lambda e, f=filename: f+e, self.graphics_extensions)
return [filename]
def sort_key(self, include):
return SCons.Node.FS._my_normcase(str(include))
def find_include(self, include, source_dir, path):
try:
sub_path = path[include[0]]
except (IndexError, KeyError):
sub_path = ()
try_names = self._latex_names(include)
for n in try_names:
# see if we find it using the path in env[var]
i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0])
if i:
return i, include
# see if we find it using the path in env['ENV'][var]
i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1])
if i:
return i, include
return i, include
def scan(self, node, path=()):
# Modify the default scan function to allow for the regular
# expression to return a comma separated list of file names
# as can be the case with the bibliography keyword.
# Cache the includes list in node so we only scan it once:
path_dict = dict(list(path))
noopt_cre = re.compile('\[.*$')
if node.includes != None:
includes = node.includes
else:
includes = self.cre.findall(node.get_contents())
# 1. Split comma-separated lines, e.g.
# ('bibliography', 'phys,comp')
# should become two entries
# ('bibliography', 'phys')
# ('bibliography', 'comp')
# 2. Remove the options, e.g., such as
# ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps')
# should become
# ('includegraphics', 'picture.eps')
split_includes = []
for include in includes:
inc_type = noopt_cre.sub('', include[0])
inc_list = string.split(include[1],',')
for j in range(len(inc_list)):
split_includes.append( (inc_type, inc_list[j]) )
#
includes = split_includes
node.includes = includes
# This is a hand-coded DSU (decorate-sort-undecorate, or
# Schwartzian transform) pattern. The sort key is the raw name
# of the file as specifed on the \include, \input, etc. line.
# TODO: what about the comment in the original Classic scanner:
# """which lets
# us keep the sort order constant regardless of whether the file
# is actually found in a Repository or locally."""
nodes = []
source_dir = node.get_dir()
for include in includes:
#
# Handle multiple filenames in include[1]
#
n, i = self.find_include(include, source_dir, path_dict)
if n is None:
# Do not bother with 'usepackage' warnings, as they most
# likely refer to system-level files
if include[0] != 'usepackage':
SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
"No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
else:
sortkey = self.sort_key(n)
nodes.append((sortkey, n))
#
nodes.sort()
nodes = map(lambda pair: pair[1], nodes)
return nodes

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/Prog.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/Prog.py 3842 2008/12/20 22:59:52 scons"
import string import string
@ -54,10 +54,8 @@ def scan(node, env, libpath = ()):
return [] return []
if SCons.Util.is_String(libs): if SCons.Util.is_String(libs):
libs = string.split(libs) libs = string.split(libs)
elif SCons.Util.is_List(libs):
libs = SCons.Util.flatten(libs)
else: else:
libs = [libs] libs = SCons.Util.flatten(libs)
try: try:
prefix = env['LIBPREFIXES'] prefix = env['LIBPREFIXES']

View file

@ -1,11 +1,12 @@
"""SCons.Scanner.C """SCons.Scanner.RC
This module implements the depenency scanner for C/C++ code. This module implements the depenency scanner for RC (Interface
Definition Language) files.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,16 +28,22 @@ This module implements the depenency scanner for C/C++ code.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/C.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/RC.py 3842 2008/12/20 22:59:52 scons"
import SCons.Node.FS import SCons.Node.FS
import SCons.Scanner import SCons.Scanner
import re
def CScanner(): def RCScan():
"""Return a prototype Scanner instance for scanning source files """Return a prototype Scanner instance for scanning RC source files"""
that use the C pre-processor"""
cs = SCons.Scanner.ClassicCPP("CScanner", res_re= r'^(?:\s*#\s*(?:include)|' \
"$CPPSUFFIXES", '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \
'\s*.*?)' \
'\s*(<|"| )([^>"\s]+)(?:[>" ])*$'
resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner",
"$RCSUFFIXES",
"CPPPATH", "CPPPATH",
'^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') res_re )
return cs
return resScanner

View file

@ -5,7 +5,7 @@ The Scanner package for the SCons software construction utility.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ The Scanner package for the SCons software construction utility.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Scanner/__init__.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Scanner/__init__.py 3842 2008/12/20 22:59:52 scons"
import re import re
import string import string
@ -67,7 +67,7 @@ class FindPathDirs:
will return all of the *path directories.""" will return all of the *path directories."""
def __init__(self, variable): def __init__(self, variable):
self.variable = variable self.variable = variable
def __call__(self, env, dir, target=None, source=None, argument=None): def __call__(self, env, dir=None, target=None, source=None, argument=None):
import SCons.PathList import SCons.PathList
try: try:
path = env[self.variable] path = env[self.variable]
@ -346,13 +346,16 @@ class Classic(Current):
def sort_key(self, include): def sort_key(self, include):
return SCons.Node.FS._my_normcase(include) return SCons.Node.FS._my_normcase(include)
def find_include_names(self, node):
return self.cre.findall(node.get_contents())
def scan(self, node, path=()): def scan(self, node, path=()):
# cache the includes list in node so we only scan it once: # cache the includes list in node so we only scan it once:
if node.includes != None: if node.includes != None:
includes = node.includes includes = node.includes
else: else:
includes = self.cre.findall(node.get_contents()) includes = self.find_include_names (node)
node.includes = includes node.includes = includes
# This is a hand-coded DSU (decorate-sort-undecorate, or # This is a hand-coded DSU (decorate-sort-undecorate, or

View file

@ -0,0 +1,376 @@
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Script/Interactive.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """
SCons interactive mode
"""
# TODO:
#
# This has the potential to grow into something with a really big life
# of its own, which might or might not be a good thing. Nevertheless,
# here are some enhancements that will probably be requested some day
# and are worth keeping in mind (assuming this takes off):
#
# - A command to re-read / re-load the SConscript files. This may
# involve allowing people to specify command-line options (e.g. -f,
# -I, --no-site-dir) that affect how the SConscript files are read.
#
# - Additional command-line options on the "build" command.
#
# Of the supported options that seemed to make sense (after a quick
# pass through the list), the ones that seemed likely enough to be
# used are listed in the man page and have explicit test scripts.
#
# These had code changed in Script/Main.py to support them, but didn't
# seem likely to be used regularly, so had no test scripts added:
#
# build --diskcheck=*
# build --implicit-cache=*
# build --implicit-deps-changed=*
# build --implicit-deps-unchanged=*
#
# These look like they should "just work" with no changes to the
# existing code, but like those above, look unlikely to be used and
# therefore had no test scripts added:
#
# build --random
#
# These I'm not sure about. They might be useful for individual
# "build" commands, and may even work, but they seem unlikely enough
# that we'll wait until they're requested before spending any time on
# writing test scripts for them, or investigating whether they work.
#
# build -q [??? is there a useful analog to the exit status?]
# build --duplicate=
# build --profile=
# build --max-drift=
# build --warn=*
# build --Y
#
# - Most of the SCons command-line options that the "build" command
# supports should be settable as default options that apply to all
# subsequent "build" commands. Maybe a "set {option}" command that
# maps to "SetOption('{option}')".
#
# - Need something in the 'help' command that prints the -h output.
#
# - A command to run the configure subsystem separately (must see how
# this interacts with the new automake model).
#
# - Command-line completion of target names; maybe even of SCons options?
# Completion is something that's supported by the Python cmd module,
# so this should be doable without too much trouble.
#
import cmd
import copy
import os
import re
import shlex
import string
import sys
try:
import readline
except ImportError:
pass
class SConsInteractiveCmd(cmd.Cmd):
"""\
build [TARGETS] Build the specified TARGETS and their dependencies.
'b' is a synonym.
clean [TARGETS] Clean (remove) the specified TARGETS and their
dependencies. 'c' is a synonym.
exit Exit SCons interactive mode.
help [COMMAND] Prints help for the specified COMMAND. 'h' and
'?' are synonyms.
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
are synonyms.
version Prints SCons version information.
"""
synonyms = {
'b' : 'build',
'c' : 'clean',
'h' : 'help',
'scons' : 'build',
'sh' : 'shell',
}
def __init__(self, **kw):
cmd.Cmd.__init__(self)
for key, val in kw.items():
setattr(self, key, val)
if sys.platform == 'win32':
self.shell_variable = 'COMSPEC'
else:
self.shell_variable = 'SHELL'
def default(self, argv):
print "*** Unknown command: %s" % argv[0]
def onecmd(self, line):
line = string.strip(line)
if not line:
print self.lastcmd
return self.emptyline()
self.lastcmd = line
if line[0] == '!':
line = 'shell ' + line[1:]
elif line[0] == '?':
line = 'help ' + line[1:]
if os.sep == '\\':
line = string.replace(line, '\\', '\\\\')
argv = shlex.split(line)
argv[0] = self.synonyms.get(argv[0], argv[0])
if not argv[0]:
return self.default(line)
else:
try:
func = getattr(self, 'do_' + argv[0])
except AttributeError:
return self.default(argv)
return func(argv)
def do_build(self, argv):
"""\
build [TARGETS] Build the specified TARGETS and their
dependencies. 'b' is a synonym.
"""
import SCons.Node
import SCons.SConsign
import SCons.Script.Main
options = copy.deepcopy(self.options)
options, targets = self.parser.parse_args(argv[1:], values=options)
SCons.Script.COMMAND_LINE_TARGETS = targets
if targets:
SCons.Script.BUILD_TARGETS = targets
else:
# If the user didn't specify any targets on the command line,
# use the list of default targets.
SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default
nodes = SCons.Script.Main._build_targets(self.fs,
options,
targets,
self.target_top)
if not nodes:
return
# Call each of the Node's alter_targets() methods, which may
# provide additional targets that ended up as part of the build
# (the canonical example being a VariantDir() when we're building
# from a source directory) and which we therefore need their
# state cleared, too.
x = []
for n in nodes:
x.extend(n.alter_targets()[0])
nodes.extend(x)
# Clean up so that we can perform the next build correctly.
#
# We do this by walking over all the children of the targets,
# and clearing their state.
#
# We currently have to re-scan each node to find their
# children, because built nodes have already been partially
# cleared and don't remember their children. (In scons
# 0.96.1 and earlier, this wasn't the case, and we didn't
# have to re-scan the nodes.)
#
# Because we have to re-scan each node, we can't clear the
# nodes as we walk over them, because we may end up rescanning
# a cleared node as we scan a later node. Therefore, only
# store the list of nodes that need to be cleared as we walk
# the tree, and clear them in a separate pass.
#
# XXX: Someone more familiar with the inner workings of scons
# may be able to point out a more efficient way to do this.
SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
seen_nodes = {}
def get_unseen_children(node, parent, seen_nodes=seen_nodes):
def is_unseen(node, seen_nodes=seen_nodes):
return not seen_nodes.has_key(node)
return filter(is_unseen, node.children(scan=1))
def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
seen_nodes[node] = 1
# If this file is in a VariantDir and has a
# corresponding source file in the source tree, remember the
# node in the source tree, too. This is needed in
# particular to clear cached implicit dependencies on the
# source file, since the scanner will scan it if the
# VariantDir was created with duplicate=0.
try:
rfile_method = node.rfile
except AttributeError:
return
else:
rfile = rfile_method()
if rfile != node:
seen_nodes[rfile] = 1
for node in nodes:
walker = SCons.Node.Walker(node,
kids_func=get_unseen_children,
eval_func=add_to_seen_nodes)
n = walker.next()
while n:
n = walker.next()
for node in seen_nodes.keys():
# Call node.clear() to clear most of the state
node.clear()
# node.clear() doesn't reset node.state, so call
# node.set_state() to reset it manually
node.set_state(SCons.Node.no_state)
node.implicit = None
# Debug: Uncomment to verify that all Taskmaster reference
# counts have been reset to zero.
#if node.ref_count != 0:
# from SCons.Debug import Trace
# Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
SCons.SConsign.Reset()
SCons.Script.Main.progress_display("scons: done clearing node information.")
def do_clean(self, argv):
"""\
clean [TARGETS] Clean (remove) the specified TARGETS
and their dependencies. 'c' is a synonym.
"""
return self.do_build(['build', '--clean'] + argv[1:])
def do_EOF(self, argv):
print
self.do_exit(argv)
def _do_one_help(self, arg):
try:
# If help_<arg>() exists, then call it.
func = getattr(self, 'help_' + arg)
except AttributeError:
try:
func = getattr(self, 'do_' + arg)
except AttributeError:
doc = None
else:
doc = self._doc_to_help(func)
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
else:
doc = self.strip_initial_spaces(func())
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
def _doc_to_help(self, obj):
doc = obj.__doc__
if doc is None:
return ''
return self._strip_initial_spaces(doc)
def _strip_initial_spaces(self, s):
#lines = s.split('\n')
lines = string.split(s, '\n')
spaces = re.match(' *', lines[0]).group(0)
#def strip_spaces(l):
# if l.startswith(spaces):
# l = l[len(spaces):]
# return l
#return '\n'.join([ strip_spaces(l) for l in lines ])
def strip_spaces(l, spaces=spaces):
if l[:len(spaces)] == spaces:
l = l[len(spaces):]
return l
lines = map(strip_spaces, lines)
return string.join(lines, '\n')
def do_exit(self, argv):
"""\
exit Exit SCons interactive mode.
"""
sys.exit(0)
def do_help(self, argv):
"""\
help [COMMAND] Prints help for the specified COMMAND. 'h'
and '?' are synonyms.
"""
if argv[1:]:
for arg in argv[1:]:
if self._do_one_help(arg):
break
else:
# If bare 'help' is called, print this class's doc
# string (if it has one).
doc = self._doc_to_help(self.__class__)
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
def do_shell(self, argv):
"""\
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
'!' are synonyms.
"""
import subprocess
argv = argv[1:]
if not argv:
argv = os.environ[self.shell_variable]
try:
p = subprocess.Popen(argv)
except EnvironmentError, e:
sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
else:
p.wait()
def do_version(self, argv):
"""\
version Prints SCons version information.
"""
sys.stdout.write(self.parser.version + '\n')
def interact(fs, parser, options, targets, target_top):
c = SConsInteractiveCmd(prompt = 'scons>>> ',
fs = fs,
parser = parser,
options = options,
targets = targets,
target_top = target_top)
c.cmdloop()

View file

@ -12,7 +12,7 @@ it goes here.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -34,9 +34,7 @@ it goes here.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Script/Main.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Script/Main.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import os import os
import os.path import os.path
@ -68,6 +66,18 @@ import SCons.Taskmaster
import SCons.Util import SCons.Util
import SCons.Warnings import SCons.Warnings
import SCons.Script.Interactive
def fetch_win32_parallel_msg():
# A subsidiary function that exists solely to isolate this import
# so we don't have to pull it in on all platforms, and so that an
# in-line "import" statement in the _main() function below doesn't
# cause warnings about local names shadowing use of the 'SCons'
# globl in nest scopes and UnboundLocalErrors and the like in some
# versions (2.1) of Python.
import SCons.Platform.win32
return SCons.Platform.win32.parallel_msg
# #
class SConsPrintHelpException(Exception): class SConsPrintHelpException(Exception):
@ -156,11 +166,16 @@ class BuildTask(SCons.Taskmaster.Task):
self.progress(self.targets[0]) self.progress(self.targets[0])
return SCons.Taskmaster.Task.prepare(self) return SCons.Taskmaster.Task.prepare(self)
def needs_execute(self):
target = self.targets[0]
if target.get_state() == SCons.Node.executing:
return True
else:
if self.top and target.has_builder():
display("scons: `%s' is up to date." % str(self.node))
return False
def execute(self): def execute(self):
for target in self.targets:
if target.get_state() == SCons.Node.up_to_date:
continue
if target.has_builder() and not hasattr(target.builder, 'status'):
if print_time: if print_time:
start_time = time.time() start_time = time.time()
global first_command_start global first_command_start
@ -174,31 +189,37 @@ class BuildTask(SCons.Taskmaster.Task):
last_command_end = finish_time last_command_end = finish_time
cumulative_command_time = cumulative_command_time+finish_time-start_time cumulative_command_time = cumulative_command_time+finish_time-start_time
sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time)) sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
break
else:
if self.top and target.has_builder():
display("scons: `%s' is up to date." % str(self.node))
def do_failed(self, status=2): def do_failed(self, status=2):
_BuildFailures.append(self.exception[1]) _BuildFailures.append(self.exception[1])
global exit_status global exit_status
global this_build_status
if self.options.ignore_errors: if self.options.ignore_errors:
SCons.Taskmaster.Task.executed(self) SCons.Taskmaster.Task.executed(self)
elif self.options.keep_going: elif self.options.keep_going:
SCons.Taskmaster.Task.fail_continue(self) SCons.Taskmaster.Task.fail_continue(self)
exit_status = status exit_status = status
this_build_status = status
else: else:
SCons.Taskmaster.Task.fail_stop(self) SCons.Taskmaster.Task.fail_stop(self)
exit_status = status exit_status = status
this_build_status = status
def executed(self): def executed(self):
t = self.targets[0] t = self.targets[0]
if self.top and not t.has_builder() and not t.side_effect: if self.top and not t.has_builder() and not t.side_effect:
if not t.exists(): if not t.exists():
sys.stderr.write("scons: *** Do not know how to make target `%s'." % t) errstr="Do not know how to make target `%s'." % t
sys.stderr.write("scons: *** " + errstr)
if not self.options.keep_going: if not self.options.keep_going:
sys.stderr.write(" Stop.") sys.stderr.write(" Stop.")
sys.stderr.write("\n") sys.stderr.write("\n")
try:
raise SCons.Errors.BuildError(t, errstr)
except KeyboardInterrupt:
raise
except:
self.exception_set()
self.do_failed() self.do_failed()
else: else:
print "scons: Nothing to be done for `%s'." % t print "scons: Nothing to be done for `%s'." % t
@ -210,54 +231,55 @@ class BuildTask(SCons.Taskmaster.Task):
# Handle the failure of a build task. The primary purpose here # Handle the failure of a build task. The primary purpose here
# is to display the various types of Errors and Exceptions # is to display the various types of Errors and Exceptions
# appropriately. # appropriately.
status = 2
exc_info = self.exc_info() exc_info = self.exc_info()
try: try:
t, e, tb = exc_info t, e, tb = exc_info
except ValueError: except ValueError:
t, e = exc_info t, e = exc_info
tb = None tb = None
if t is None: if t is None:
# The Taskmaster didn't record an exception for this Task; # The Taskmaster didn't record an exception for this Task;
# see if the sys module has one. # see if the sys module has one.
t, e = sys.exc_info()[:2] try:
t, e, tb = sys.exc_info()[:]
except ValueError:
t, e = exc_info
tb = None
def nodestring(n): # Deprecated string exceptions will have their string stored
if not SCons.Util.is_List(n): # in the first entry of the tuple.
n = [ n ]
return string.join(map(str, n), ', ')
errfmt = "scons: *** [%s] %s\n"
if t == SCons.Errors.BuildError:
tname = nodestring(e.node)
errstr = e.errstr
if e.filename:
errstr = e.filename + ': ' + errstr
sys.stderr.write(errfmt % (tname, errstr))
elif t == SCons.Errors.TaskmasterException:
tname = nodestring(e.node)
sys.stderr.write(errfmt % (tname, e.errstr))
type, value, trace = e.exc_info
traceback.print_exception(type, value, trace)
elif t == SCons.Errors.ExplicitExit:
status = e.status
tname = nodestring(e.node)
errstr = 'Explicit exit, status %s' % status
sys.stderr.write(errfmt % (tname, errstr))
else:
if e is None: if e is None:
e = t e = t
s = str(e)
if t == SCons.Errors.StopError and not self.options.keep_going:
s = s + ' Stop.'
sys.stderr.write("scons: *** %s\n" % s)
if tb and print_stacktrace: buildError = SCons.Errors.convert_to_BuildError(e)
if not buildError.node:
buildError.node = self.node
node = buildError.node
if not SCons.Util.is_List(node):
node = [ node ]
nodename = string.join(map(str, node), ', ')
errfmt = "scons: *** [%s] %s\n"
sys.stderr.write(errfmt % (nodename, buildError))
if (buildError.exc_info[2] and buildError.exc_info[1] and
# TODO(1.5)
#not isinstance(
# buildError.exc_info[1],
# (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))):
not isinstance(buildError.exc_info[1], EnvironmentError) and
not isinstance(buildError.exc_info[1], SCons.Errors.StopError) and
not isinstance(buildError.exc_info[1], SCons.Errors.UserError)):
type, value, trace = buildError.exc_info
traceback.print_exception(type, value, trace)
elif tb and print_stacktrace:
sys.stderr.write("scons: internal stack trace:\n") sys.stderr.write("scons: internal stack trace:\n")
traceback.print_tb(tb, file=sys.stderr) traceback.print_tb(tb, file=sys.stderr)
self.do_failed(status) self.exception = (e, buildError, tb) # type, value, traceback
self.do_failed(buildError.exitstatus)
self.exc_clear() self.exc_clear()
@ -366,7 +388,9 @@ class QuestionTask(SCons.Taskmaster.Task):
if self.targets[0].get_state() != SCons.Node.up_to_date or \ if self.targets[0].get_state() != SCons.Node.up_to_date or \
(self.top and not self.targets[0].exists()): (self.top and not self.targets[0].exists()):
global exit_status global exit_status
global this_build_status
exit_status = 1 exit_status = 1
this_build_status = 1
self.tm.stop() self.tm.stop()
def executed(self): def executed(self):
@ -392,6 +416,16 @@ class TreePrinter:
SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
def python_version_string():
return string.split(sys.version)[0]
def python_version_unsupported(version=sys.version_info):
return version < (1, 5, 2)
def python_version_deprecated(version=sys.version_info):
return version < (2, 2, 0)
# Global variables # Global variables
print_objects = 0 print_objects = 0
@ -400,7 +434,8 @@ print_stacktrace = 0
print_time = 0 print_time = 0
sconscript_time = 0 sconscript_time = 0
cumulative_command_time = 0 cumulative_command_time = 0
exit_status = 0 # exit status, assume success by default exit_status = 0 # final exit status, assume success by default
this_build_status = 0 # "exit status" of an individual build
num_jobs = None num_jobs = None
delayed_warnings = [] delayed_warnings = []
@ -566,57 +601,14 @@ def _scons_internal_error():
traceback.print_exc() traceback.print_exc()
sys.exit(2) sys.exit(2)
def _setup_warn(arg): def _SConstruct_exists(dirname='', repositories=[], filelist=None):
"""The --warn option. An argument to this option
should be of the form <warning-class> or no-<warning-class>.
The warning class is munged in order to get an actual class
name from the SCons.Warnings module to enable or disable.
The supplied <warning-class> is split on hyphens, each element
is captialized, then smushed back together. Then the string
"SCons.Warnings." is added to the front and "Warning" is added
to the back to get the fully qualified class name.
For example, --warn=deprecated will enable the
SCons.Warnings.DeprecatedWarning class.
--warn=no-dependency will disable the
SCons.Warnings.DependencyWarning class.
As a special case, --warn=all and --warn=no-all
will enable or disable (respectively) the base
class of all warnings, which is SCons.Warning.Warning."""
elems = string.split(string.lower(arg), '-')
enable = 1
if elems[0] == 'no':
enable = 0
del elems[0]
if len(elems) == 1 and elems[0] == 'all':
class_name = "Warning"
else:
def _capitalize(s):
if s[:5] == "scons":
return "SCons" + s[5:]
else:
return string.capitalize(s)
class_name = string.join(map(_capitalize, elems), '') + "Warning"
try:
clazz = getattr(SCons.Warnings, class_name)
except AttributeError:
sys.stderr.write("No warning type: '%s'\n" % arg)
else:
if enable:
SCons.Warnings.enableWarningClass(clazz)
else:
SCons.Warnings.suppressWarningClass(clazz)
def _SConstruct_exists(dirname='', repositories=[]):
"""This function checks that an SConstruct file exists in a directory. """This function checks that an SConstruct file exists in a directory.
If so, it returns the path of the file. By default, it checks the If so, it returns the path of the file. By default, it checks the
current directory. current directory.
""" """
for file in ['SConstruct', 'Sconstruct', 'sconstruct']: if not filelist:
filelist = ['SConstruct', 'Sconstruct', 'sconstruct']
for file in filelist:
sfile = os.path.join(dirname, file) sfile = os.path.join(dirname, file)
if os.path.isfile(sfile): if os.path.isfile(sfile):
return sfile return sfile
@ -730,8 +722,8 @@ def version_string(label, module):
module.__buildsys__) module.__buildsys__)
def _main(parser): def _main(parser):
import SCons
global exit_status global exit_status
global this_build_status
options = parser.values options = parser.values
@ -745,17 +737,22 @@ def _main(parser):
default_warnings = [ SCons.Warnings.CorruptSConsignWarning, default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
SCons.Warnings.DeprecatedWarning, SCons.Warnings.DeprecatedWarning,
SCons.Warnings.DuplicateEnvironmentWarning, SCons.Warnings.DuplicateEnvironmentWarning,
SCons.Warnings.FutureReservedVariableWarning,
SCons.Warnings.LinkWarning,
SCons.Warnings.MissingSConscriptWarning, SCons.Warnings.MissingSConscriptWarning,
SCons.Warnings.NoMD5ModuleWarning, SCons.Warnings.NoMD5ModuleWarning,
SCons.Warnings.NoMetaclassSupportWarning, SCons.Warnings.NoMetaclassSupportWarning,
SCons.Warnings.NoObjectCountWarning, SCons.Warnings.NoObjectCountWarning,
SCons.Warnings.NoParallelSupportWarning, SCons.Warnings.NoParallelSupportWarning,
SCons.Warnings.MisleadingKeywordsWarning, ] SCons.Warnings.MisleadingKeywordsWarning,
SCons.Warnings.ReservedVariableWarning,
SCons.Warnings.StackSizeWarning,
]
for warning in default_warnings: for warning in default_warnings:
SCons.Warnings.enableWarningClass(warning) SCons.Warnings.enableWarningClass(warning)
SCons.Warnings._warningOut = _scons_internal_warning SCons.Warnings._warningOut = _scons_internal_warning
if options.warn: SCons.Warnings.process_warn_strings(options.warn)
_setup_warn(options.warn)
# Now that we have the warnings configuration set up, we can actually # Now that we have the warnings configuration set up, we can actually
# issue (or suppress) any warnings about warning-worthy things that # issue (or suppress) any warnings about warning-worthy things that
@ -788,13 +785,15 @@ def _main(parser):
if options.climb_up: if options.climb_up:
target_top = '.' # directory to prepend to targets target_top = '.' # directory to prepend to targets
script_dir = os.getcwd() # location of script script_dir = os.getcwd() # location of script
while script_dir and not _SConstruct_exists(script_dir, options.repository): while script_dir and not _SConstruct_exists(script_dir,
options.repository,
options.file):
script_dir, last_part = os.path.split(script_dir) script_dir, last_part = os.path.split(script_dir)
if last_part: if last_part:
target_top = os.path.join(last_part, target_top) target_top = os.path.join(last_part, target_top)
else: else:
script_dir = '' script_dir = ''
if script_dir: if script_dir and script_dir != os.getcwd():
display("scons: Entering directory `%s'" % script_dir) display("scons: Entering directory `%s'" % script_dir)
os.chdir(script_dir) os.chdir(script_dir)
@ -813,7 +812,8 @@ def _main(parser):
if options.file: if options.file:
scripts.extend(options.file) scripts.extend(options.file)
if not scripts: if not scripts:
sfile = _SConstruct_exists(repositories=options.repository) sfile = _SConstruct_exists(repositories=options.repository,
filelist=options.file)
if sfile: if sfile:
scripts.append(sfile) scripts.append(sfile)
@ -835,10 +835,10 @@ def _main(parser):
SCons.Node.implicit_cache = options.implicit_cache SCons.Node.implicit_cache = options.implicit_cache
SCons.Node.implicit_deps_changed = options.implicit_deps_changed SCons.Node.implicit_deps_changed = options.implicit_deps_changed
SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
if options.no_exec: if options.no_exec:
SCons.SConf.dryrun = 1 SCons.SConf.dryrun = 1
SCons.Action.execute_actions = None SCons.Action.execute_actions = None
CleanTask.execute = CleanTask.show
if options.question: if options.question:
SCons.SConf.dryrun = 1 SCons.SConf.dryrun = 1
if options.clean: if options.clean:
@ -850,19 +850,6 @@ def _main(parser):
if options.no_progress or options.silent: if options.no_progress or options.silent:
progress_display.set_mode(0) progress_display.set_mode(0)
if options.silent:
display.set_mode(0)
if options.silent:
SCons.Action.print_actions = None
if options.cache_disable:
SCons.CacheDir.CacheDir = SCons.Util.Null()
if options.cache_debug:
SCons.CacheDir.cache_debug = options.cache_debug
if options.cache_force:
SCons.CacheDir.cache_force = True
if options.cache_show:
SCons.CacheDir.cache_show = True
if options.site_dir: if options.site_dir:
_load_site_scons_dir(d, options.site_dir) _load_site_scons_dir(d, options.site_dir)
@ -887,7 +874,18 @@ def _main(parser):
SCons.Script._Add_Targets(targets + parser.rargs) SCons.Script._Add_Targets(targets + parser.rargs)
SCons.Script._Add_Arguments(xmit_args) SCons.Script._Add_Arguments(xmit_args)
# If stdout is not a tty, replace it with a wrapper object to call flush
# after every write.
#
# Tty devices automatically flush after every newline, so the replacement
# isn't necessary. Furthermore, if we replace sys.stdout, the readline
# module will no longer work. This affects the behavior during
# --interactive mode. --interactive should only be used when stdin and
# stdout refer to a tty.
if not sys.stdout.isatty():
sys.stdout = SCons.Util.Unbuffered(sys.stdout) sys.stdout = SCons.Util.Unbuffered(sys.stdout)
if not sys.stderr.isatty():
sys.stderr = SCons.Util.Unbuffered(sys.stderr)
memory_stats.append('before reading SConscript files:') memory_stats.append('before reading SConscript files:')
count_stats.append(('pre-', 'read')) count_stats.append(('pre-', 'read'))
@ -902,7 +900,7 @@ def _main(parser):
SCons.Script._SConscript._SConscript(fs, script) SCons.Script._SConscript._SConscript(fs, script)
except SCons.Errors.StopError, e: except SCons.Errors.StopError, e:
# We had problems reading an SConscript file, such as it # We had problems reading an SConscript file, such as it
# couldn't be copied in to the BuildDir. Since we're just # couldn't be copied in to the VariantDir. Since we're just
# reading SConscript files and haven't started building # reading SConscript files and haven't started building
# things yet, stop regardless of whether they used -i or -k # things yet, stop regardless of whether they used -i or -k
# or anything else. # or anything else.
@ -917,6 +915,26 @@ def _main(parser):
memory_stats.append('after reading SConscript files:') memory_stats.append('after reading SConscript files:')
count_stats.append(('post-', 'read')) count_stats.append(('post-', 'read'))
# Re-{enable,disable} warnings in case they disabled some in
# the SConscript file.
#
# We delay enabling the PythonVersionWarning class until here so that,
# if they explicity disabled it in either in the command line or in
# $SCONSFLAGS, or in the SConscript file, then the search through
# the list of deprecated warning classes will find that disabling
# first and not issue the warning.
SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning)
SCons.Warnings.process_warn_strings(options.warn)
# Now that we've read the SConscript files, we can check for the
# warning about deprecated Python versions--delayed until here
# in case they disabled the warning in the SConscript files.
if python_version_deprecated():
msg = "Support for pre-2.2 Python (%s) is deprecated.\n" + \
" If this will cause hardship, contact dev@scons.tigris.org."
SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
msg % python_version_string())
if not options.help: if not options.help:
SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
@ -943,7 +961,7 @@ def _main(parser):
# Change directory to the top-level SConstruct directory, then tell # Change directory to the top-level SConstruct directory, then tell
# the Node.FS subsystem that we're all done reading the SConscript # the Node.FS subsystem that we're all done reading the SConscript
# files and calling Repository() and BuildDir() and changing # files and calling Repository() and VariantDir() and changing
# directories and the like, so it can go ahead and start memoizing # directories and the like, so it can go ahead and start memoizing
# the string values of file system nodes. # the string values of file system nodes.
@ -957,6 +975,49 @@ def _main(parser):
SCons.Node.FS.set_duplicate(options.duplicate) SCons.Node.FS.set_duplicate(options.duplicate)
fs.set_max_drift(options.max_drift) fs.set_max_drift(options.max_drift)
SCons.Job.explicit_stack_size = options.stack_size
if options.md5_chunksize:
SCons.Node.FS.File.md5_chunksize = options.md5_chunksize
platform = SCons.Platform.platform_module()
if options.interactive:
SCons.Script.Interactive.interact(fs, OptionsParser, options,
targets, target_top)
else:
# Build the targets
nodes = _build_targets(fs, options, targets, target_top)
if not nodes:
exit_status = 2
def _build_targets(fs, options, targets, target_top):
global this_build_status
this_build_status = 0
progress_display.set_mode(not (options.no_progress or options.silent))
display.set_mode(not options.silent)
SCons.Action.print_actions = not options.silent
SCons.Action.execute_actions = not options.no_exec
SCons.Node.FS.do_store_info = not options.no_exec
SCons.SConf.dryrun = options.no_exec
if options.diskcheck:
SCons.Node.FS.set_diskcheck(options.diskcheck)
SCons.CacheDir.cache_enabled = not options.cache_disable
SCons.CacheDir.cache_debug = options.cache_debug
SCons.CacheDir.cache_force = options.cache_force
SCons.CacheDir.cache_show = options.cache_show
if options.no_exec:
CleanTask.execute = CleanTask.show
else:
CleanTask.execute = CleanTask.remove
lookup_top = None lookup_top = None
if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
# They specified targets on the command line or modified # They specified targets on the command line or modified
@ -1003,7 +1064,7 @@ def _main(parser):
if not targets: if not targets:
sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
sys.exit(2) return None
def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
if isinstance(x, SCons.Node.Node): if isinstance(x, SCons.Node.Node):
@ -1046,7 +1107,7 @@ def _main(parser):
opening_message = "Cleaning targets ..." opening_message = "Cleaning targets ..."
closing_message = "done cleaning targets." closing_message = "done cleaning targets."
if options.keep_going: if options.keep_going:
closing_message = "done cleaning targets (errors occurred during clean)." failure_message = "done cleaning targets (errors occurred during clean)."
else: else:
failure_message = "cleaning terminated because of errors." failure_message = "cleaning terminated because of errors."
except AttributeError: except AttributeError:
@ -1091,29 +1152,43 @@ def _main(parser):
msg = "parallel builds are unsupported by this version of Python;\n" + \ msg = "parallel builds are unsupported by this version of Python;\n" + \
"\tignoring -j or num_jobs option.\n" "\tignoring -j or num_jobs option.\n"
elif sys.platform == 'win32': elif sys.platform == 'win32':
import SCons.Platform.win32 msg = fetch_win32_parallel_msg()
msg = SCons.Platform.win32.parallel_msg
if msg: if msg:
SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
memory_stats.append('before building targets:') memory_stats.append('before building targets:')
count_stats.append(('pre-', 'build')) count_stats.append(('pre-', 'build'))
try: def jobs_postfunc(
progress_display("scons: " + opening_message) jobs=jobs,
jobs.run() options=options,
finally: closing_message=closing_message,
jobs.cleanup() failure_message=failure_message
if exit_status: ):
if jobs.were_interrupted():
progress_display("scons: Build interrupted.")
global exit_status
global this_build_status
exit_status = 2
this_build_status = 2
if this_build_status:
progress_display("scons: " + failure_message) progress_display("scons: " + failure_message)
else: else:
progress_display("scons: " + closing_message) progress_display("scons: " + closing_message)
if not options.no_exec: if not options.no_exec:
if jobs.were_interrupted():
progress_display("scons: writing .sconsign file.")
SCons.SConsign.write() SCons.SConsign.write()
progress_display("scons: " + opening_message)
jobs.run(postfunc = jobs_postfunc)
memory_stats.append('after building targets:') memory_stats.append('after building targets:')
count_stats.append(('post-', 'build')) count_stats.append(('post-', 'build'))
return nodes
def _exec_main(parser, values): def _exec_main(parser, values):
sconsflags = os.environ.get('SCONSFLAGS', '') sconsflags = os.environ.get('SCONSFLAGS', '')
all_args = string.split(sconsflags) + sys.argv[1:] all_args = string.split(sconsflags) + sys.argv[1:]
@ -1124,6 +1199,9 @@ def _exec_main(parser, values):
import pdb import pdb
pdb.Pdb().runcall(_main, parser) pdb.Pdb().runcall(_main, parser)
elif options.profile_file: elif options.profile_file:
try:
from cProfile import Profile
except ImportError, e:
from profile import Profile from profile import Profile
# Some versions of Python 2.4 shipped a profiler that had the # Some versions of Python 2.4 shipped a profiler that had the
@ -1155,17 +1233,25 @@ def main():
global exit_status global exit_status
global first_command_start global first_command_start
# Check up front for a Python version we do not support. We
# delay the check for deprecated Python versions until later,
# after the SConscript files have been read, in case they
# disable that warning.
if python_version_unsupported():
msg = "scons: *** SCons version %s does not run under Python version %s.\n"
sys.stderr.write(msg % (SCons.__version__, python_version_string()))
sys.exit(1)
parts = ["SCons by Steven Knight et al.:\n"] parts = ["SCons by Steven Knight et al.:\n"]
try: try:
import __main__
parts.append(version_string("script", __main__)) parts.append(version_string("script", __main__))
except KeyboardInterrupt: except (ImportError, AttributeError):
raise
except:
# On Windows there is no scons.py, so there is no # On Windows there is no scons.py, so there is no
# __main__.__version__, hence there is no script version. # __main__.__version__, hence there is no script version.
pass pass
parts.append(version_string("engine", SCons)) parts.append(version_string("engine", SCons))
parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation") parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation")
version = string.join(parts, '') version = string.join(parts, '')
import SConsOptions import SConsOptions
@ -1180,7 +1266,7 @@ def main():
if s: if s:
exit_status = s exit_status = s
except KeyboardInterrupt: except KeyboardInterrupt:
print "Build interrupted." print("scons: Build interrupted.")
sys.exit(2) sys.exit(2)
except SyntaxError, e: except SyntaxError, e:
_scons_syntax_error(e) _scons_syntax_error(e)
@ -1191,6 +1277,8 @@ def main():
except SConsPrintHelpException: except SConsPrintHelpException:
parser.print_help() parser.print_help()
exit_status = 0 exit_status = 0
except SCons.Errors.BuildError, e:
exit_status = e.exitstatus
except: except:
# An exception here is likely a builtin Python exception Python # An exception here is likely a builtin Python exception Python
# code in an SConscript file. Show them precisely what the # code in an SConscript file. Show them precisely what the

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,15 +21,21 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Script/SConsOptions.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Script/SConsOptions.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import optparse import optparse
import re
import string import string
import sys import sys
import textwrap import textwrap
try:
no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')
except re.error:
# Pre-2.0 Python versions don't have the (?<= negative
# look-behind assertion.
no_hyphen_re = re.compile(r'(\s+|-*\w{2,}-(?=\w{2,}))')
try: try:
from gettext import gettext from gettext import gettext
except ImportError: except ImportError:
@ -38,6 +44,7 @@ except ImportError:
_ = gettext _ = gettext
import SCons.Node.FS import SCons.Node.FS
import SCons.Warnings
OptionValueError = optparse.OptionValueError OptionValueError = optparse.OptionValueError
SUPPRESS_HELP = optparse.SUPPRESS_HELP SUPPRESS_HELP = optparse.SUPPRESS_HELP
@ -119,9 +126,12 @@ class SConsValues(optparse.Values):
'help', 'help',
'implicit_cache', 'implicit_cache',
'max_drift', 'max_drift',
'md5_chunksize',
'no_exec', 'no_exec',
'num_jobs', 'num_jobs',
'random', 'random',
'stack_size',
'warn',
] ]
def set_option(self, name, value): def set_option(self, name, value):
@ -163,6 +173,21 @@ class SConsValues(optparse.Values):
# Set this right away so it can affect the rest of the # Set this right away so it can affect the rest of the
# file/Node lookups while processing the SConscript files. # file/Node lookups while processing the SConscript files.
SCons.Node.FS.set_diskcheck(value) SCons.Node.FS.set_diskcheck(value)
elif name == 'stack_size':
try:
value = int(value)
except ValueError:
raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
elif name == 'md5_chunksize':
try:
value = int(value)
except ValueError:
raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
elif name == 'warn':
if SCons.Util.is_String(value):
value = [value]
value = self.__SConscript_settings__.get(name, []) + value
SCons.Warnings.process_warn_strings(value)
self.__SConscript_settings__[name] = value self.__SConscript_settings__[name] = value
@ -369,8 +394,16 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
def format_option(self, option): def format_option(self, option):
""" """
A copy of the normal optparse.IndentedHelpFormatter.format_option() A copy of the normal optparse.IndentedHelpFormatter.format_option()
method, snarfed so we can set the subsequent_indent on the method. This has been snarfed so we can modify text wrapping to
textwrap.wrap() call below... out liking:
-- add our own regular expression that doesn't break on hyphens
(so things like --no-print-directory don't get broken);
-- wrap the list of options themselves when it's too long
(the wrapper.fill(opts) call below);
-- set the subsequent_indent when wrapping the help_text.
""" """
# The help for each option consists of two parts: # The help for each option consists of two parts:
# * the opt strings and metavars # * the opt strings and metavars
@ -397,7 +430,11 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
opt_width = self.help_position - self.current_indent - 2 opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width: if len(opts) > opt_width:
opts = "%*s%s\n" % (self.current_indent, "", opts) wrapper = textwrap.TextWrapper(width=self.width,
initial_indent = ' ',
subsequent_indent = ' ')
wrapper.wordsep_re = no_hyphen_re
opts = wrapper.fill(opts) + '\n'
indent_first = self.help_position indent_first = self.help_position
else: # start help on same line as opts else: # start help on same line as opts
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
@ -415,8 +452,10 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
help_text = expand_default(option) help_text = expand_default(option)
# SCons: indent every line of the help text but the first. # SCons: indent every line of the help text but the first.
help_lines = textwrap.wrap(help_text, self.help_width, wrapper = textwrap.TextWrapper(width=self.help_width,
subsequent_indent = ' ') subsequent_indent = ' ')
wrapper.wordsep_re = no_hyphen_re
help_lines = wrapper.wrap(help_text)
result.append("%*s%s\n" % (indent_first, "", help_lines[0])) result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
for line in help_lines[1:]: for line in help_lines[1:]:
result.append("%*s%s\n" % (self.help_position, "", line)) result.append("%*s%s\n" % (self.help_position, "", line))
@ -466,6 +505,7 @@ def Parser(version):
usage="usage: scons [OPTION] [TARGET] ...",) usage="usage: scons [OPTION] [TARGET] ...",)
op.preserve_unknown_options = True op.preserve_unknown_options = True
op.version = version
# Add the options to the parser we just created. # Add the options to the parser we just created.
# #
@ -489,8 +529,13 @@ def Parser(version):
# options ignored for compatibility # options ignored for compatibility
def opt_ignore(option, opt, value, parser): def opt_ignore(option, opt, value, parser):
sys.stderr.write("Warning: ignoring %s option\n" % opt) sys.stderr.write("Warning: ignoring %s option\n" % opt)
op.add_option("-b", "-m", "-S", "-t", op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
"--no-keep-going", "--stop", "--touch", "--environment-overrides",
"--no-keep-going",
"--no-print-directory",
"--print-directory",
"--stop",
"--touch",
action="callback", callback=opt_ignore, action="callback", callback=opt_ignore,
help="Ignored for compatibility.") help="Ignored for compatibility.")
@ -543,34 +588,30 @@ def Parser(version):
help = opt_config_help, help = opt_config_help,
metavar="MODE") metavar="MODE")
def opt_not_yet(option, opt, value, parser):
sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt)
sys.exit(0)
op.add_option('-d',
action="callback", callback=opt_not_yet,
help = "Print file dependency information.")
op.add_option('-D', op.add_option('-D',
dest="climb_up", default=None, dest="climb_up", default=None,
action="store_const", const=2, action="store_const", const=2,
help="Search up directory tree for SConstruct, " help="Search up directory tree for SConstruct, "
"build all Default() targets.") "build all Default() targets.")
debug_options = ["count", "dtree", "explain", "findlibs",
"includes", "memoizer", "memory", "objects",
"pdb", "presub", "stacktrace", "stree",
"time", "tree"]
deprecated_debug_options = { deprecated_debug_options = {
"dtree" : '; please use --tree=derived instead',
"nomemoizer" : ' and has no effect', "nomemoizer" : ' and has no effect',
"stree" : '; please use --tree=all,status instead',
"tree" : '; please use --tree=all instead',
} }
debug_options = ["count", "explain", "findlibs",
"includes", "memoizer", "memory", "objects",
"pdb", "presub", "stacktrace",
"time"] + deprecated_debug_options.keys()
def opt_debug(option, opt, value, parser, def opt_debug(option, opt, value, parser,
debug_options=debug_options, debug_options=debug_options,
deprecated_debug_options=deprecated_debug_options): deprecated_debug_options=deprecated_debug_options):
if value in debug_options: if value in debug_options:
parser.values.debug.append(value) parser.values.debug.append(value)
elif value in deprecated_debug_options.keys(): if value in deprecated_debug_options.keys():
try: try:
parser.values.delayed_warnings parser.values.delayed_warnings
except AttributeError: except AttributeError:
@ -667,6 +708,11 @@ def Parser(version):
action="callback", callback=opt_implicit_deps, action="callback", callback=opt_implicit_deps,
help="Ignore changes in implicit dependencies.") help="Ignore changes in implicit dependencies.")
op.add_option('--interact', '--interactive',
dest='interactive', default=False,
action="store_true",
help="Run in interactive mode.")
op.add_option('-j', '--jobs', op.add_option('-j', '--jobs',
nargs=1, type="int", nargs=1, type="int",
dest="num_jobs", default=1, dest="num_jobs", default=1,
@ -686,6 +732,13 @@ def Parser(version):
help="Set maximum system clock drift to N seconds.", help="Set maximum system clock drift to N seconds.",
metavar="N") metavar="N")
op.add_option('--md5-chunksize',
nargs=1, type="int",
dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize,
action="store",
help="Set chunk-size for MD5 signature computation to N kilobytes.",
metavar="N")
op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon',
dest='no_exec', default=False, dest='no_exec', default=False,
action="store_true", action="store_true",
@ -730,6 +783,13 @@ def Parser(version):
help="Use DIR instead of the usual site_scons dir.", help="Use DIR instead of the usual site_scons dir.",
metavar="DIR") metavar="DIR")
op.add_option('--stack-size',
nargs=1, type="int",
dest='stack_size',
action="store",
help="Set the stack size of the threads used to run jobs to N kilobytes.",
metavar="N")
op.add_option('--taskmastertrace', op.add_option('--taskmastertrace',
nargs=1, nargs=1,
dest="taskmastertrace_file", default=None, dest="taskmastertrace_file", default=None,
@ -777,17 +837,22 @@ def Parser(version):
help="Search up directory tree for SConstruct, " help="Search up directory tree for SConstruct, "
"build Default() targets from local SConscript.") "build Default() targets from local SConscript.")
def opt_version(option, opt, value, parser, version=version): def opt_version(option, opt, value, parser):
sys.stdout.write(version + '\n') sys.stdout.write(parser.version + '\n')
sys.exit(0) sys.exit(0)
op.add_option("-v", "--version", op.add_option("-v", "--version",
action="callback", callback=opt_version, action="callback", callback=opt_version,
help="Print the SCons version number and exit.") help="Print the SCons version number and exit.")
def opt_warn(option, opt, value, parser, tree_options=tree_options):
if SCons.Util.is_String(value):
value = string.split(value, ',')
parser.values.warn.extend(value)
op.add_option('--warn', '--warning', op.add_option('--warn', '--warning',
nargs=1, nargs=1, type="string",
dest="warn", default=None, dest="warn", default=[],
action="store", action="callback", callback=opt_warn,
help="Enable or disable warnings.", help="Enable or disable warnings.",
metavar="WARNING-SPEC") metavar="WARNING-SPEC")
@ -802,11 +867,12 @@ def Parser(version):
# we don't want to change. These all get a "the -X option is not # we don't want to change. These all get a "the -X option is not
# yet implemented" message and don't show up in the help output. # yet implemented" message and don't show up in the help output.
op.add_option('-e', '--environment-overrides', def opt_not_yet(option, opt, value, parser):
dest="environment_overrides", msg = "Warning: the %s option is not yet implemented\n" % opt
action="callback", callback=opt_not_yet, sys.stderr.write(msg)
# help="Environment variables override makefiles." sys.exit(0)
help=SUPPRESS_HELP)
op.add_option('-l', '--load-average', '--max-load', op.add_option('-l', '--load-average', '--max-load',
nargs=1, type="int", nargs=1, type="int",
dest="load_average", default=0, dest="load_average", default=0,
@ -853,16 +919,6 @@ def Parser(version):
dest="no_builtin_rules", dest="no_builtin_rules",
# help="Clear default environments and variables." # help="Clear default environments and variables."
help=SUPPRESS_HELP) help=SUPPRESS_HELP)
op.add_option('-w', '--print-directory',
action="callback", callback=opt_not_yet,
dest="print_directory",
# help="Print the current directory."
help=SUPPRESS_HELP)
op.add_option('--no-print-directory',
action="callback", callback=opt_not_yet,
dest="no_print_directory",
# help="Turn off -w, even if it was turned on implicitly."
help=SUPPRESS_HELP)
op.add_option('--write-filenames', op.add_option('--write-filenames',
nargs=1, type="string", nargs=1, type="string",
dest="write_filenames", dest="write_filenames",

View file

@ -6,7 +6,7 @@ files.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -28,7 +28,7 @@ files.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Script/SConscript.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Script/SConscript.py 3842 2008/12/20 22:59:52 scons"
import SCons import SCons
import SCons.Action import SCons.Action
@ -39,7 +39,6 @@ import SCons.Errors
import SCons.Node import SCons.Node
import SCons.Node.Alias import SCons.Node.Alias
import SCons.Node.FS import SCons.Node.FS
import SCons.Options
import SCons.Platform import SCons.Platform
import SCons.SConf import SCons.SConf
import SCons.Script.Main import SCons.Script.Main
@ -82,7 +81,10 @@ def get_calling_namespaces():
"""Return the locals and globals for the function that called """Return the locals and globals for the function that called
into this module in the current call stack.""" into this module in the current call stack."""
try: 1/0 try: 1/0
except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame 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
# Find the first frame that *isn't* from this file. This means # Find the first frame that *isn't* from this file. This means
# that we expect all of the SCons frames that implement an Export() # that we expect all of the SCons frames that implement an Export()
@ -141,7 +143,8 @@ call_stack = []
def Return(*vars, **kw): def Return(*vars, **kw):
retval = [] retval = []
try: try:
for var in vars: fvars = SCons.Util.flatten(vars)
for var in fvars:
for v in string.split(var): for v in string.split(var):
retval.append(call_stack[-1].globals[v]) retval.append(call_stack[-1].globals[v])
except KeyError, x: except KeyError, x:
@ -276,7 +279,20 @@ def _SConscript(fs, *files, **kw):
fs.chdir(frame.prev_dir, change_os_dir=0) fs.chdir(frame.prev_dir, change_os_dir=0)
rdir = frame.prev_dir.rdir() rdir = frame.prev_dir.rdir()
rdir._create() # Make sure there's a directory there. rdir._create() # Make sure there's a directory there.
try:
os.chdir(rdir.get_abspath()) os.chdir(rdir.get_abspath())
except OSError, e:
# We still couldn't chdir there, so raise the error,
# but only if actions are being executed.
#
# If the -n option was used, the directory would *not*
# have been created and we should just carry on and
# let things muddle through. This isn't guaranteed
# to work if the SConscript files are reading things
# from disk (for example), but it should work well
# enough for most configurations.
if SCons.Action.execute_actions:
raise e
results.append(frame.retval) results.append(frame.retval)
@ -403,16 +419,16 @@ class SConsEnvironment(SCons.Environment.Base):
if kw.get('exports'): if kw.get('exports'):
exports.extend(self.Split(kw['exports'])) exports.extend(self.Split(kw['exports']))
build_dir = kw.get('build_dir') variant_dir = kw.get('variant_dir') or kw.get('build_dir')
if build_dir: if variant_dir:
if len(files) != 1: if len(files) != 1:
raise SCons.Errors.UserError, \ raise SCons.Errors.UserError, \
"Invalid SConscript() usage - can only specify one SConscript with a build_dir" "Invalid SConscript() usage - can only specify one SConscript with a variant_dir"
duplicate = kw.get('duplicate', 1) duplicate = kw.get('duplicate', 1)
src_dir = kw.get('src_dir') src_dir = kw.get('src_dir')
if not src_dir: if not src_dir:
src_dir, fname = os.path.split(str(files[0])) src_dir, fname = os.path.split(str(files[0]))
files = [os.path.join(str(build_dir), fname)] files = [os.path.join(str(variant_dir), fname)]
else: else:
if not isinstance(src_dir, SCons.Node.Node): if not isinstance(src_dir, SCons.Node.Node):
src_dir = self.fs.Dir(src_dir) src_dir = self.fs.Dir(src_dir)
@ -422,11 +438,11 @@ class SConsEnvironment(SCons.Environment.Base):
if fn.is_under(src_dir): if fn.is_under(src_dir):
# Get path relative to the source directory. # Get path relative to the source directory.
fname = fn.get_path(src_dir) fname = fn.get_path(src_dir)
files = [os.path.join(str(build_dir), fname)] files = [os.path.join(str(variant_dir), fname)]
else: else:
files = [fn.abspath] files = [fn.abspath]
kw['src_dir'] = build_dir kw['src_dir'] = variant_dir
self.fs.BuildDir(build_dir, src_dir, duplicate) self.fs.VariantDir(variant_dir, src_dir, duplicate)
return (files, exports) return (files, exports)

View file

@ -12,7 +12,7 @@ it goes here.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -34,7 +34,7 @@ it goes here.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Script/__init__.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Script/__init__.py 3842 2008/12/20 22:59:52 scons"
import time import time
start_time = time.time() start_time = time.time()
@ -84,6 +84,7 @@ import SCons.SConf
import SCons.Subst import SCons.Subst
import SCons.Tool import SCons.Tool
import SCons.Util import SCons.Util
import SCons.Variables
import SCons.Defaults import SCons.Defaults
import Main import Main
@ -138,22 +139,31 @@ call_stack = _SConscript.call_stack
Action = SCons.Action.Action Action = SCons.Action.Action
AddMethod = SCons.Util.AddMethod AddMethod = SCons.Util.AddMethod
AllowSubstExceptions = SCons.Subst.SetAllowableExceptions AllowSubstExceptions = SCons.Subst.SetAllowableExceptions
BoolOption = SCons.Options.BoolOption
Builder = SCons.Builder.Builder Builder = SCons.Builder.Builder
Configure = _SConscript.Configure Configure = _SConscript.Configure
EnumOption = SCons.Options.EnumOption
Environment = SCons.Environment.Environment Environment = SCons.Environment.Environment
#OptParser = SCons.SConsOptions.OptParser #OptParser = SCons.SConsOptions.OptParser
FindPathDirs = SCons.Scanner.FindPathDirs FindPathDirs = SCons.Scanner.FindPathDirs
ListOption = SCons.Options.ListOption
PackageOption = SCons.Options.PackageOption
PathOption = SCons.Options.PathOption
Platform = SCons.Platform.Platform Platform = SCons.Platform.Platform
Return = _SConscript.Return Return = _SConscript.Return
Scanner = SCons.Scanner.Base Scanner = SCons.Scanner.Base
Tool = SCons.Tool.Tool Tool = SCons.Tool.Tool
WhereIs = SCons.Util.WhereIs WhereIs = SCons.Util.WhereIs
#
BoolVariable = SCons.Variables.BoolVariable
EnumVariable = SCons.Variables.EnumVariable
ListVariable = SCons.Variables.ListVariable
PackageVariable = SCons.Variables.PackageVariable
PathVariable = SCons.Variables.PathVariable
# Deprecated names that will go away some day.
BoolOption = SCons.Options.BoolOption
EnumOption = SCons.Options.EnumOption
ListOption = SCons.Options.ListOption
PackageOption = SCons.Options.PackageOption
PathOption = SCons.Options.PathOption
# Action factories. # Action factories.
Chmod = SCons.Defaults.Chmod Chmod = SCons.Defaults.Chmod
Copy = SCons.Defaults.Copy Copy = SCons.Defaults.Copy
@ -262,6 +272,9 @@ def HelpFunction(text):
sconscript_reading = 0 sconscript_reading = 0
# #
def Variables(files=[], args=ARGUMENTS):
return SCons.Variables.Variables(files, args)
def Options(files=[], args=ARGUMENTS): def Options(files=[], args=ARGUMENTS):
return SCons.Options.Options(files, args) return SCons.Options.Options(files, args)
@ -321,6 +334,7 @@ GlobalDefaultEnvironmentFunctions = [
'Tag', 'Tag',
'TargetSignatures', 'TargetSignatures',
'Value', 'Value',
'VariantDir',
] ]
GlobalDefaultBuilders = [ GlobalDefaultBuilders = [

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,7 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Sig.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Sig.py 3842 2008/12/20 22:59:52 scons"
__doc__ = """Place-holder for the old SCons.Sig module hierarchy __doc__ = """Place-holder for the old SCons.Sig module hierarchy

View file

@ -5,7 +5,7 @@ SCons string substitution.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,9 +27,7 @@ SCons string substitution.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Subst.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Subst.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
import re import re
import string import string
@ -39,11 +37,11 @@ import UserString
import SCons.Errors import SCons.Errors
from SCons.Util import is_String, is_List, is_Tuple from SCons.Util import is_String, is_Sequence
# Indexed by the SUBST_* constants below. # Indexed by the SUBST_* constants below.
_strconv = [SCons.Util.to_String, _strconv = [SCons.Util.to_String_for_subst,
SCons.Util.to_String, SCons.Util.to_String_for_subst,
SCons.Util.to_String_for_signature] SCons.Util.to_String_for_signature]
@ -188,7 +186,7 @@ class NLWrapper:
list = self.list list = self.list
if list is None: if list is None:
list = [] list = []
elif not is_List(list) and not is_Tuple(list): elif not is_Sequence(list):
list = [list] list = [list]
# The map(self.func) call is what actually turns # The map(self.func) call is what actually turns
# a list into appropriate proxies. # a list into appropriate proxies.
@ -203,10 +201,10 @@ class Targets_or_Sources(UserList.UserList):
wrapping a NLWrapper. This class handles the different methods used wrapping a NLWrapper. This class handles the different methods used
to access the list, calling the NLWrapper to create proxies on demand. to access the list, calling the NLWrapper to create proxies on demand.
Note that we subclass UserList.UserList purely so that the is_List() Note that we subclass UserList.UserList purely so that the
function will identify an object of this class as a list during is_Sequence() function will identify an object of this class as
variable expansion. We're not really using any UserList.UserList a list during variable expansion. We're not really using any
methods in practice. UserList.UserList methods in practice.
""" """
def __init__(self, nl): def __init__(self, nl):
self.nl = nl self.nl = nl
@ -272,7 +270,13 @@ def subst_dict(target, source):
dict = {} dict = {}
if target: if target:
tnl = NLWrapper(target, lambda x: x.get_subst_proxy()) def get_tgt_subst_proxy(thing):
try:
subst_proxy = thing.get_subst_proxy()
except AttributeError:
subst_proxy = thing # probably a string, just return it
return subst_proxy
tnl = NLWrapper(target, get_tgt_subst_proxy)
dict['TARGETS'] = Targets_or_Sources(tnl) dict['TARGETS'] = Targets_or_Sources(tnl)
dict['TARGET'] = Target_or_Source(tnl) dict['TARGET'] = Target_or_Source(tnl)
else: else:
@ -287,7 +291,10 @@ def subst_dict(target, source):
pass pass
else: else:
node = rfile() node = rfile()
try:
return node.get_subst_proxy() return node.get_subst_proxy()
except AttributeError:
return node # probably a String, just return it
snl = NLWrapper(source, get_src_subst_proxy) snl = NLWrapper(source, get_src_subst_proxy)
dict['SOURCES'] = Targets_or_Sources(snl) dict['SOURCES'] = Targets_or_Sources(snl)
dict['SOURCE'] = Target_or_Source(snl) dict['SOURCE'] = Target_or_Source(snl)
@ -312,6 +319,25 @@ _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)')
# Indexed by the SUBST_* constants above. # Indexed by the SUBST_* constants above.
_regex_remove = [ _rm, None, _remove ] _regex_remove = [ _rm, None, _remove ]
def _rm_list(list):
#return [ l for l in list if not l in ('$(', '$)') ]
return filter(lambda l: not l in ('$(', '$)'), list)
def _remove_list(list):
result = []
do_append = result.append
for l in list:
if l == '$(':
do_append = lambda x: None
elif l == '$)':
do_append = result.append
else:
do_append(l)
return result
# Indexed by the SUBST_* constants above.
_list_remove = [ _rm_list, None, _remove_list ]
# Regular expressions for splitting strings and handling substitutions, # Regular expressions for splitting strings and handling substitutions,
# for use by the scons_subst() and scons_subst_list() functions: # for use by the scons_subst() and scons_subst_list() functions:
# #
@ -342,7 +368,8 @@ _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
_space_sep = re.compile(r'[\t ]+(?![^{]*})') _space_sep = re.compile(r'[\t ]+(?![^{]*})')
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
"""Expand a string containing construction variable substitutions. """Expand a string or list containing construction variable
substitutions.
This is the work-horse function for substitutions in file names This is the work-horse function for substitutions in file names
and the like. The companion scons_subst_list() function (below) and the like. The companion scons_subst_list() function (below)
@ -427,11 +454,10 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
var = string.split(key, '.')[0] var = string.split(key, '.')[0]
lv[var] = '' lv[var] = ''
return self.substitute(s, lv) return self.substitute(s, lv)
elif is_List(s) or is_Tuple(s): elif is_Sequence(s):
def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
return conv(substitute(l, lvars)) return conv(substitute(l, lvars))
r = map(func, s) return map(func, s)
return string.join(r)
elif callable(s): elif callable(s):
try: try:
s = s(target=self.target, s = s(target=self.target,
@ -458,6 +484,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
separate tokens. separate tokens.
""" """
if is_String(args) and not isinstance(args, CmdStringHolder): if is_String(args) and not isinstance(args, CmdStringHolder):
args = str(args) # In case it's a UserString.
try: try:
def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars): def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars):
return conv(expand(match.group(1), lvars)) return conv(expand(match.group(1), lvars))
@ -472,11 +499,10 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
result = [] result = []
for a in args: for a in args:
result.append(self.conv(self.expand(a, lvars))) result.append(self.conv(self.expand(a, lvars)))
try:
result = string.join(result, '')
except TypeError:
if len(result) == 1: if len(result) == 1:
result = result[0] result = result[0]
else:
result = string.join(map(str, result), '')
return result return result
else: else:
return self.expand(args, lvars) return self.expand(args, lvars)
@ -524,6 +550,10 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
# Compress strings of white space characters into # Compress strings of white space characters into
# a single space. # a single space.
result = string.strip(_space_sep.sub(' ', result)) result = string.strip(_space_sep.sub(' ', result))
elif is_Sequence(result):
remove = _list_remove[mode]
if remove:
result = remove(result)
return result return result
@ -542,7 +572,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
# except KeyError: # except KeyError:
# Subst_List_Strings[strSubst] = 1 # Subst_List_Strings[strSubst] = 1
# import SCons.Debug # import SCons.Debug
# SCons.Debug.caller(1) # SCons.Debug.caller_trace(1)
class ListSubber(UserList.UserList): class ListSubber(UserList.UserList):
"""A class to construct the results of a scons_subst_list() call. """A class to construct the results of a scons_subst_list() call.
@ -634,7 +664,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
lv[var] = '' lv[var] = ''
self.substitute(s, lv, 0) self.substitute(s, lv, 0)
self.this_word() self.this_word()
elif is_List(s) or is_Tuple(s): elif is_Sequence(s):
for a in s: for a in s:
self.substitute(a, lvars, 1) self.substitute(a, lvars, 1)
self.next_word() self.next_word()
@ -666,6 +696,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
""" """
if is_String(args) and not isinstance(args, CmdStringHolder): if is_String(args) and not isinstance(args, CmdStringHolder):
args = str(args) # In case it's a UserString.
args = _separate_args.findall(args) args = _separate_args.findall(args)
for a in args: for a in args:
if a[0] in ' \t\n\r\f\v': if a[0] in ' \t\n\r\f\v':
@ -827,18 +858,18 @@ def scons_subst_once(strSubst, env, key):
a = match.group(1) a = match.group(1)
if a in matchlist: if a in matchlist:
a = val a = val
if is_List(a) or is_Tuple(a): if is_Sequence(a):
return string.join(map(str, a)) return string.join(map(str, a))
else: else:
return str(a) return str(a)
if is_List(strSubst) or is_Tuple(strSubst): if is_Sequence(strSubst):
result = [] result = []
for arg in strSubst: for arg in strSubst:
if is_String(arg): if is_String(arg):
if arg in matchlist: if arg in matchlist:
arg = val arg = val
if is_List(arg) or is_Tuple(arg): if is_Sequence(arg):
result.extend(arg) result.extend(arg)
else: else:
result.append(arg) result.append(arg)

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -48,20 +48,24 @@ interface and the SCons build engine. There are two key classes here:
target(s) that it decides need to be evaluated and/or built. target(s) that it decides need to be evaluated and/or built.
""" """
__revision__ = "src/engine/SCons/Taskmaster.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Taskmaster.py 3842 2008/12/20 22:59:52 scons"
import SCons.compat
from itertools import chain
import operator import operator
import string import string
import sys import sys
import traceback import traceback
import SCons.Node
import SCons.Errors import SCons.Errors
import SCons.Node
StateString = SCons.Node.StateString StateString = SCons.Node.StateString
NODE_NO_STATE = SCons.Node.no_state
NODE_PENDING = SCons.Node.pending
NODE_EXECUTING = SCons.Node.executing
NODE_UP_TO_DATE = SCons.Node.up_to_date
NODE_EXECUTED = SCons.Node.executed
NODE_FAILED = SCons.Node.failed
# A subsystem for recording stats about how different Nodes are handled by # A subsystem for recording stats about how different Nodes are handled by
@ -134,6 +138,10 @@ class Task:
self.node = node self.node = node
self.exc_clear() self.exc_clear()
def trace_message(self, method, node, description='node'):
fmt = '%-20s %s %s\n'
return fmt % (method + ':', description, self.tm.trace_node(node))
def display(self, message): def display(self, message):
""" """
Hook to allow the calling interface to display a message. Hook to allow the calling interface to display a message.
@ -155,6 +163,8 @@ class Task:
unlink underlying files and make all necessary directories before unlink underlying files and make all necessary directories before
the Action is actually called to build the targets. the Action is actually called to build the targets.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.prepare()', self.node))
# Now that it's the appropriate time, give the TaskMaster a # Now that it's the appropriate time, give the TaskMaster a
# chance to raise any exceptions it encountered while preparing # chance to raise any exceptions it encountered while preparing
@ -165,6 +175,17 @@ class Task:
self.display(self.tm.message) self.display(self.tm.message)
self.tm.message = None self.tm.message = None
# Let the targets take care of any necessary preparations.
# This includes verifying that all of the necessary sources
# and dependencies exist, removing the target file(s), etc.
#
# As of April 2008, the get_executor().prepare() method makes
# sure that all of the aggregate sources necessary to build this
# Task's target(s) exist in one up-front check. The individual
# target t.prepare() methods check that each target's explicit
# or implicit dependencies exists, and also initialize the
# .sconsign info.
self.targets[0].get_executor().prepare()
for t in self.targets: for t in self.targets:
t.prepare() t.prepare()
for s in t.side_effects: for s in t.side_effects:
@ -175,6 +196,17 @@ class Task:
""" """
return self.node return self.node
def needs_execute(self):
"""
Called to determine whether the task's execute() method should
be run.
This method allows one to skip the somethat costly execution
of the execute() method in a seperate thread. For example,
that would be unnecessary for up-to-date targets.
"""
return True
def execute(self): def execute(self):
""" """
Called to execute the task. Called to execute the task.
@ -183,6 +215,8 @@ class Task:
so only do thread safe stuff here. Do thread unsafe stuff in so only do thread safe stuff here. Do thread unsafe stuff in
prepare(), executed() or failed(). prepare(), executed() or failed().
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.execute()', self.node))
try: try:
everything_was_cached = 1 everything_was_cached = 1
@ -192,8 +226,6 @@ class Task:
break break
if not everything_was_cached: if not everything_was_cached:
self.targets[0].build() self.targets[0].build()
except KeyboardInterrupt:
raise
except SystemExit: except SystemExit:
exc_value = sys.exc_info()[1] exc_value = sys.exc_info()[1]
raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code)
@ -201,9 +233,11 @@ class Task:
raise raise
except SCons.Errors.BuildError: except SCons.Errors.BuildError:
raise raise
except: except Exception, e:
raise SCons.Errors.TaskmasterException(self.targets[0], buildError = SCons.Errors.convert_to_BuildError(e)
sys.exc_info()) buildError.node = self.targets[0]
buildError.exc_info = sys.exc_info()
raise buildError
def executed_without_callbacks(self): def executed_without_callbacks(self):
""" """
@ -211,11 +245,15 @@ class Task:
and the Taskmaster instance doesn't want to call and the Taskmaster instance doesn't want to call
the Node's callback methods. the Node's callback methods.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.executed_without_callbacks()',
self.node))
for t in self.targets: for t in self.targets:
if t.get_state() == SCons.Node.executing: if t.get_state() == NODE_EXECUTING:
for side_effect in t.side_effects: for side_effect in t.side_effects:
side_effect.set_state(SCons.Node.no_state) side_effect.set_state(NODE_NO_STATE)
t.set_state(SCons.Node.executed) t.set_state(NODE_EXECUTED)
def executed_with_callbacks(self): def executed_with_callbacks(self):
""" """
@ -230,11 +268,15 @@ class Task:
post-visit actions that must take place regardless of whether post-visit actions that must take place regardless of whether
or not the target was an actual built target or a source Node. or not the target was an actual built target or a source Node.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.executed_with_callbacks()',
self.node))
for t in self.targets: for t in self.targets:
if t.get_state() == SCons.Node.executing: if t.get_state() == NODE_EXECUTING:
for side_effect in t.side_effects: for side_effect in t.side_effects:
side_effect.set_state(SCons.Node.no_state) side_effect.set_state(NODE_NO_STATE)
t.set_state(SCons.Node.executed) t.set_state(NODE_EXECUTED)
t.built() t.built()
t.visited() t.visited()
@ -243,15 +285,32 @@ class Task:
def failed(self): def failed(self):
""" """
Default action when a task fails: stop the build. Default action when a task fails: stop the build.
Note: Although this function is normally invoked on nodes in
the executing state, it might also be invoked on up-to-date
nodes when using Configure().
""" """
self.fail_stop() self.fail_stop()
def fail_stop(self): def fail_stop(self):
""" """
Explicit stop-the-build failure. Explicit stop-the-build failure.
This sets failure status on the target nodes and all of
their dependent parent nodes.
Note: Although this function is normally invoked on nodes in
the executing state, it might also be invoked on up-to-date
nodes when using Configure().
""" """
for t in self.targets: T = self.tm.trace
t.set_state(SCons.Node.failed) if T: T.write(self.trace_message('Task.failed_stop()', self.node))
# Invoke will_not_build() to clean-up the pending children
# list.
self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED))
# Tell the taskmaster to not start any new tasks
self.tm.stop() self.tm.stop()
# We're stopping because of a build failure, but give the # We're stopping because of a build failure, but give the
@ -266,12 +325,15 @@ class Task:
This sets failure status on the target nodes and all of This sets failure status on the target nodes and all of
their dependent parent nodes. their dependent parent nodes.
Note: Although this function is normally invoked on nodes in
the executing state, it might also be invoked on up-to-date
nodes when using Configure().
""" """
for t in self.targets: T = self.tm.trace
# Set failure state on all of the parents that were dependent if T: T.write(self.trace_message('Task.failed_continue()', self.node))
# on this failed build.
def set_state(node): node.set_state(SCons.Node.failed) self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED))
t.call_for_all_waiting_parents(set_state)
def make_ready_all(self): def make_ready_all(self):
""" """
@ -280,11 +342,14 @@ class Task:
This is used when the interface needs every target Node to be This is used when the interface needs every target Node to be
visited--the canonical example being the "scons -c" option. visited--the canonical example being the "scons -c" option.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.make_ready_all()', self.node))
self.out_of_date = self.targets[:] self.out_of_date = self.targets[:]
for t in self.targets: for t in self.targets:
t.disambiguate().set_state(SCons.Node.executing) t.disambiguate().set_state(NODE_EXECUTING)
for s in t.side_effects: for s in t.side_effects:
s.set_state(SCons.Node.executing) s.set_state(NODE_EXECUTING)
def make_ready_current(self): def make_ready_current(self):
""" """
@ -293,7 +358,12 @@ class Task:
This is the default behavior for building only what's necessary. This is the default behavior for building only what's necessary.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.make_ready_current()',
self.node))
self.out_of_date = [] self.out_of_date = []
needs_executing = False
for t in self.targets: for t in self.targets:
try: try:
t.disambiguate().make_ready() t.disambiguate().make_ready()
@ -301,13 +371,24 @@ class Task:
(not t.always_build and t.is_up_to_date()) (not t.always_build and t.is_up_to_date())
except EnvironmentError, e: except EnvironmentError, e:
raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename)
if is_up_to_date:
t.set_state(SCons.Node.up_to_date) if not is_up_to_date:
else:
self.out_of_date.append(t) self.out_of_date.append(t)
t.set_state(SCons.Node.executing) needs_executing = True
if needs_executing:
for t in self.targets:
t.set_state(NODE_EXECUTING)
for s in t.side_effects: for s in t.side_effects:
s.set_state(SCons.Node.executing) s.set_state(NODE_EXECUTING)
else:
for t in self.targets:
# We must invoke visited() to ensure that the node
# information has been computed before allowing the
# parent nodes to execute. (That could occur in a
# parallel build...)
t.visited()
t.set_state(NODE_UP_TO_DATE)
make_ready = make_ready_current make_ready = make_ready_current
@ -321,6 +402,8 @@ class Task:
waiting parent Nodes, or Nodes waiting on a common side effect, waiting parent Nodes, or Nodes waiting on a common side effect,
that can be put back on the candidates list. that can be put back on the candidates list.
""" """
T = self.tm.trace
if T: T.write(self.trace_message('Task.postprocess()', self.node))
# We may have built multiple targets, some of which may have # We may have built multiple targets, some of which may have
# common parents waiting for this build. Count up how many # common parents waiting for this build. Count up how many
@ -331,24 +414,34 @@ class Task:
targets = set(self.targets) targets = set(self.targets)
pending_children = self.tm.pending_children
parents = {} parents = {}
for t in targets: for t in targets:
for p in t.waiting_parents.keys(): # A node can only be in the pending_children set if it has
# some waiting_parents.
if t.waiting_parents:
if T: T.write(self.trace_message('Task.postprocess()',
t,
'removing'))
pending_children.discard(t)
for p in t.waiting_parents:
parents[p] = parents.get(p, 0) + 1 parents[p] = parents.get(p, 0) + 1
for t in targets: for t in targets:
for s in t.side_effects: for s in t.side_effects:
if s.get_state() == SCons.Node.executing: if s.get_state() == NODE_EXECUTING:
s.set_state(SCons.Node.no_state) s.set_state(NODE_NO_STATE)
for p in s.waiting_parents.keys(): for p in s.waiting_parents:
if not parents.has_key(p): parents[p] = parents.get(p, 0) + 1
parents[p] = 1 for p in s.waiting_s_e:
for p in s.waiting_s_e.keys():
if p.ref_count == 0: if p.ref_count == 0:
self.tm.candidates.append(p) self.tm.candidates.append(p)
for p, subtract in parents.items(): for p, subtract in parents.items():
p.ref_count = p.ref_count - subtract p.ref_count = p.ref_count - subtract
if T: T.write(self.trace_message('Task.postprocess()',
p,
'adjusted parent ref count'))
if p.ref_count == 0: if p.ref_count == 0:
self.tm.candidates.append(p) self.tm.candidates.append(p)
@ -409,12 +502,15 @@ class Task:
raise exc_type, exc_value, exc_traceback raise exc_type, exc_value, exc_traceback
def find_cycle(stack): def find_cycle(stack, visited):
if stack[-1] in visited:
return None
visited.add(stack[-1])
for n in stack[-1].waiting_parents:
stack.append(n)
if stack[0] == stack[-1]: if stack[0] == stack[-1]:
return stack return stack
for n in stack[-1].waiting_parents.keys(): if find_cycle(stack, visited):
stack.append(n)
if find_cycle(stack):
return stack return stack
stack.pop() stack.pop()
return None return None
@ -437,6 +533,7 @@ class Taskmaster:
self.message = None self.message = None
self.trace = trace self.trace = trace
self.next_candidate = self.find_next_candidate self.next_candidate = self.find_next_candidate
self.pending_children = set()
def find_next_candidate(self): def find_next_candidate(self):
""" """
@ -477,9 +574,104 @@ class Taskmaster:
def no_next_candidate(self): def no_next_candidate(self):
""" """
Stops Taskmaster processing by not returning a next candidate. Stops Taskmaster processing by not returning a next candidate.
Note that we have to clean-up the Taskmaster candidate list
because the cycle detection depends on the fact all nodes have
been processed somehow.
""" """
while self.candidates:
candidates = self.candidates
self.candidates = []
self.will_not_build(candidates)
return None return None
def _validate_pending_children(self):
"""
Validate the content of the pending_children set. Assert if an
internal error is found.
This function is used strictly for debugging the taskmaster by
checking that no invariants are violated. It is not used in
normal operation.
The pending_children set is used to detect cycles in the
dependency graph. We call a "pending child" a child that is
found in the "pending" state when checking the dependencies of
its parent node.
A pending child can occur when the Taskmaster completes a loop
through a cycle. For example, lets imagine a graph made of
three node (A, B and C) making a cycle. The evaluation starts
at node A. The taskmaster first consider whether node A's
child B is up-to-date. Then, recursively, node B needs to
check whether node C is up-to-date. This leaves us with a
dependency graph looking like:
Next candidate \
\
Node A (Pending) --> Node B(Pending) --> Node C (NoState)
^ |
| |
+-------------------------------------+
Now, when the Taskmaster examines the Node C's child Node A,
it finds that Node A is in the "pending" state. Therefore,
Node A is a pending child of node C.
Pending children indicate that the Taskmaster has potentially
loop back through a cycle. We say potentially because it could
also occur when a DAG is evaluated in parallel. For example,
consider the following graph:
Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ...
| ^
| |
+----------> Node D (NoState) --------+
/
Next candidate /
The Taskmaster first evaluates the nodes A, B, and C and
starts building some children of node C. Assuming, that the
maximum parallel level has not been reached, the Taskmaster
will examine Node D. It will find that Node C is a pending
child of Node D.
In summary, evaluating a graph with a cycle will always
involve a pending child at one point. A pending child might
indicate either a cycle or a diamond-shaped DAG. Only a
fraction of the nodes ends-up being a "pending child" of
another node. This keeps the pending_children set small in
practice.
We can differentiate between the two cases if we wait until
the end of the build. At this point, all the pending children
nodes due to a diamond-shaped DAG will have been properly
built (or will have failed to build). But, the pending
children involved in a cycle will still be in the pending
state.
The taskmaster removes nodes from the pending_children set as
soon as a pending_children node moves out of the pending
state. This also helps to keep the pending_children set small.
"""
for n in self.pending_children:
assert n.state in (NODE_PENDING, NODE_EXECUTING), \
(str(n), StateString[n.state])
assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents))
for p in n.waiting_parents:
assert p.ref_count > 0, (str(n), str(p), p.ref_count)
def trace_message(self, message):
return 'Taskmaster: %s\n' % message
def trace_node(self, node):
return '<%-10s %-3s %s>' % (StateString[node.get_state()],
node.ref_count,
repr(str(node)))
def _find_next_ready_node(self): def _find_next_ready_node(self):
""" """
Finds the next node that is ready to be built. Finds the next node that is ready to be built.
@ -505,15 +697,25 @@ class Taskmaster:
self.ready_exc = None self.ready_exc = None
T = self.trace T = self.trace
if T: T.write('\n' + self.trace_message('Looking for a node to evaluate'))
while 1: while 1:
node = self.next_candidate() node = self.next_candidate()
if node is None: if node is None:
if T: T.write(self.trace_message('No candidate anymore.') + '\n')
return None return None
node = node.disambiguate() node = node.disambiguate()
state = node.get_state() state = node.get_state()
# For debugging only:
#
# try:
# self._validate_pending_children()
# except:
# self.ready_exc = sys.exc_info()
# return node
if CollectStats: if CollectStats:
if not hasattr(node, 'stats'): if not hasattr(node, 'stats'):
node.stats = Stats() node.stats = Stats()
@ -523,119 +725,138 @@ class Taskmaster:
else: else:
S = None S = None
if T: T.write('Taskmaster: %s:' % repr(str(node))) if T: T.write(self.trace_message(' Considering node %s and its children:' % self.trace_node(node)))
if state == NODE_NO_STATE:
# Mark this node as being on the execution stack:
node.set_state(NODE_PENDING)
elif state > NODE_PENDING:
# Skip this node if it has already been evaluated: # Skip this node if it has already been evaluated:
if state > SCons.Node.pending:
if S: S.already_handled = S.already_handled + 1 if S: S.already_handled = S.already_handled + 1
if T: T.write(' already handled (%s)\n' % StateString[state]) if T: T.write(self.trace_message(' already handled (executed)'))
continue continue
# Mark this node as being on the execution stack:
node.set_state(SCons.Node.pending)
try: try:
children = node.children() + node.prerequisites children = node.children()
except SystemExit: except SystemExit:
exc_value = sys.exc_info()[1] exc_value = sys.exc_info()[1]
e = SCons.Errors.ExplicitExit(node, exc_value.code) e = SCons.Errors.ExplicitExit(node, exc_value.code)
self.ready_exc = (SCons.Errors.ExplicitExit, e) self.ready_exc = (SCons.Errors.ExplicitExit, e)
if T: T.write(' SystemExit\n') if T: T.write(self.trace_message(' SystemExit'))
return node return node
except KeyboardInterrupt: except Exception, e:
if T: T.write(' KeyboardInterrupt\n')
raise
except:
# We had a problem just trying to figure out the # We had a problem just trying to figure out the
# children (like a child couldn't be linked in to a # children (like a child couldn't be linked in to a
# BuildDir, or a Scanner threw something). Arrange to # VariantDir, or a Scanner threw something). Arrange to
# raise the exception when the Task is "executed." # raise the exception when the Task is "executed."
self.ready_exc = sys.exc_info() self.ready_exc = sys.exc_info()
if S: S.problem = S.problem + 1 if S: S.problem = S.problem + 1
if T: T.write(' exception\n') if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e))
return node return node
if T and children: children_not_visited = []
c = map(str, children) children_pending = set()
c.sort() children_not_ready = []
T.write(' children:\n %s\n ' % c) children_failed = False
childstate = map(lambda N: (N, N.get_state()), children) for child in chain(children,node.prerequisites):
childstate = child.get_state()
if T: T.write(self.trace_message(' ' + self.trace_node(child)))
if childstate == NODE_NO_STATE:
children_not_visited.append(child)
elif childstate == NODE_PENDING:
children_pending.add(child)
elif childstate == NODE_FAILED:
children_failed = True
if childstate <= NODE_EXECUTING:
children_not_ready.append(child)
# These nodes have not even been visited yet. Add
# them to the list so that on some next pass we can
# take a stab at evaluating them (or their children).
children_not_visited.reverse()
self.candidates.extend(self.order(children_not_visited))
#if T and children_not_visited:
# T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited)))
# T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates)))
# Skip this node if any of its children have failed.
#
# This catches the case where we're descending a top-level
# target and one of our children failed while trying to be
# built by a *previous* descent of an earlier top-level
# target.
#
# It can also occur if a node is reused in multiple
# targets. One first descends though the one of the
# target, the next time occurs through the other target.
#
# Note that we can only have failed_children if the
# --keep-going flag was used, because without it the build
# will stop before diving in the other branch.
#
# Note that even if one of the children fails, we still
# added the other children to the list of candidate nodes
# to keep on building (--keep-going).
if children_failed:
node.set_state(NODE_FAILED)
# Skip this node if any of its children have failed. This
# catches the case where we're descending a top-level target
# and one of our children failed while trying to be built
# by a *previous* descent of an earlier top-level target.
failed_children = filter(lambda I: I[1] == SCons.Node.failed,
childstate)
if failed_children:
node.set_state(SCons.Node.failed)
if S: S.child_failed = S.child_failed + 1 if S: S.child_failed = S.child_failed + 1
if T: if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node)))
c = map(str, failed_children)
c.sort()
T.write(' children failed:\n %s\n' % c)
continue continue
# Detect dependency cycles: if children_not_ready:
pending_nodes = filter(lambda I: I[1] == SCons.Node.pending, childstate) for child in children_not_ready:
if pending_nodes: # We're waiting on one or more derived targets
for p in pending_nodes: # that have not yet finished building.
cycle = find_cycle([p[0], node])
if cycle:
desc = "Dependency cycle: " + string.join(map(str, cycle), " -> ")
if T: T.write(' dependency cycle\n')
raise SCons.Errors.UserError, desc
not_built = filter(lambda I: I[1] <= SCons.Node.executing, childstate)
if not_built:
# We're waiting on one or more derived targets that have
# not yet finished building.
not_visited = filter(lambda I: not I[1], not_built)
if not_visited:
# Some of them haven't even been visited yet.
# Add them to the list so that on some next pass
# we can take a stab at evaluating them (or
# their children).
not_visited = map(lambda I: I[0], not_visited)
not_visited.reverse()
self.candidates.extend(self.order(not_visited))
n_b_nodes = map(lambda I: I[0], not_built)
# Add this node to the waiting parents lists of anything
# we're waiting on, with a reference count so we can be
# put back on the list for re-evaluation when they've
# all finished.
map(lambda n, P=node: n.add_to_waiting_parents(P), n_b_nodes)
node.ref_count = len(set(n_b_nodes))
if S: S.not_built = S.not_built + 1 if S: S.not_built = S.not_built + 1
# Add this node to the waiting parents lists of
# anything we're waiting on, with a reference
# count so we can be put back on the list for
# re-evaluation when they've all finished.
node.ref_count = node.ref_count + child.add_to_waiting_parents(node)
if T: T.write(self.trace_message(' adjusted ref count: %s, child %s' %
(self.trace_node(node), repr(str(child)))))
if T: if T:
c = map(str, n_b_nodes) for pc in children_pending:
c.sort() T.write(self.trace_message(' adding %s to the pending children set\n' %
T.write(' waiting on unfinished children:\n %s\n' % c) self.trace_node(pc)))
self.pending_children = self.pending_children | children_pending
continue continue
# Skip this node if it has side-effects that are # Skip this node if it has side-effects that are
# currently being built: # currently being built:
side_effects = filter(lambda N: wait_side_effects = False
N.get_state() == SCons.Node.executing, for se in node.side_effects:
node.side_effects) if se.get_state() == NODE_EXECUTING:
if side_effects: se.add_to_waiting_s_e(node)
map(lambda n, P=node: n.add_to_waiting_s_e(P), side_effects) wait_side_effects = True
if wait_side_effects:
if S: S.side_effects = S.side_effects + 1 if S: S.side_effects = S.side_effects + 1
if T:
c = map(str, side_effects)
c.sort()
T.write(' waiting on side effects:\n %s\n' % c)
continue continue
# The default when we've gotten through all of the checks above: # The default when we've gotten through all of the checks above:
# this node is ready to be built. # this node is ready to be built.
if S: S.build = S.build + 1 if S: S.build = S.build + 1
if T: T.write(' evaluating %s\n' % node) if T: T.write(self.trace_message('Evaluating %s\n' %
self.trace_node(node)))
# For debugging only:
#
# try:
# self._validate_pending_children()
# except:
# self.ready_exc = sys.exc_info()
# return node
return node return node
return None return None
@ -657,11 +878,9 @@ class Taskmaster:
task = self.tasker(self, tlist, node in self.original_top, node) task = self.tasker(self, tlist, node in self.original_top, node)
try: try:
task.make_ready() task.make_ready()
except KeyboardInterrupt:
raise
except: except:
# We had a problem just trying to get this task ready (like # We had a problem just trying to get this task ready (like
# a child couldn't be linked in to a BuildDir when deciding # a child couldn't be linked in to a VariantDir when deciding
# whether this node is current). Arrange to raise the # whether this node is current). Arrange to raise the
# exception when the Task is "executed." # exception when the Task is "executed."
self.ready_exc = sys.exc_info() self.ready_exc = sys.exc_info()
@ -673,8 +892,94 @@ class Taskmaster:
return task return task
def will_not_build(self, nodes, node_func=lambda n: None):
"""
Perform clean-up about nodes that will never be built. Invokes
a user defined function on all of these nodes (including all
of their parents).
"""
T = self.trace
pending_children = self.pending_children
to_visit = set(nodes)
pending_children = pending_children - to_visit
if T:
for n in nodes:
T.write(self.trace_message(' removing node %s from the pending children set\n' %
self.trace_node(n)))
try:
while 1:
try:
node = to_visit.pop()
except AttributeError:
# Python 1.5.2
if len(to_visit):
node = to_visit[0]
to_visit.remove(node)
else:
break
node_func(node)
# Prune recursion by flushing the waiting children
# list immediately.
parents = node.waiting_parents
node.waiting_parents = set()
to_visit = to_visit | parents
pending_children = pending_children - parents
for p in parents:
p.ref_count = p.ref_count - 1
if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' %
self.trace_node(p)))
except KeyError:
# The container to_visit has been emptied.
pass
# We have the stick back the pending_children list into the
# task master because the python 1.5.2 compatibility does not
# allow us to use in-place updates
self.pending_children = pending_children
def stop(self): def stop(self):
""" """
Stops the current build completely. Stops the current build completely.
""" """
self.next_candidate = self.no_next_candidate self.next_candidate = self.no_next_candidate
def cleanup(self):
"""
Check for dependency cycles.
"""
if not self.pending_children:
return
# TODO(1.5)
#nclist = [ (n, find_cycle([n], set())) for n in self.pending_children ]
nclist = map(lambda n: (n, find_cycle([n], set())), self.pending_children)
# TODO(1.5)
#genuine_cycles = [
# node for node, cycle in nclist
# if cycle or node.get_state() != NODE_EXECUTED
#]
genuine_cycles = filter(lambda t: t[1] or t[0].get_state() != NODE_EXECUTED, nclist)
if not genuine_cycles:
# All of the "cycles" found were single nodes in EXECUTED state,
# which is to say, they really weren't cycles. Just return.
return
desc = 'Found dependency cycle(s):\n'
for node, cycle in nclist:
if cycle:
desc = desc + " " + string.join(map(str, cycle), " -> ") + "\n"
else:
desc = desc + \
" Internal Error: no cycle found for node %s (%s) in state %s\n" % \
(node, repr(node), StateString[node.get_state()])
raise SCons.Errors.UserError, desc

View file

@ -10,7 +10,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -32,7 +32,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/386asm.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/386asm.py 3842 2008/12/20 22:59:52 scons"
from SCons.Tool.PharLapCommon import addPharLapPaths from SCons.Tool.PharLapCommon import addPharLapPaths
import SCons.Util import SCons.Util

View file

@ -10,7 +10,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -32,7 +32,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/BitKeeper.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/BitKeeper.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Builder import SCons.Builder

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/CVS.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/CVS.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Builder import SCons.Builder

View file

@ -0,0 +1,241 @@
"""SCons.Tool.FortranCommon
Stuff for processing Fortran, common to all fortran dialects.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/FortranCommon.py 3842 2008/12/20 22:59:52 scons"
import re
import string
import os.path
import SCons.Action
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
def isfortran(env, source):
"""Return 1 if any of code in source has fortran files in it, 0
otherwise."""
try:
fsuffixes = env['FORTRANSUFFIXES']
except KeyError:
# If no FORTRANSUFFIXES, no fortran tool, so there is no need to look
# for fortran sources.
return 0
if not source:
# Source might be None for unusual cases like SConf.
return 0
for s in source:
if s.sources:
ext = os.path.splitext(str(s.sources[0]))[1]
if ext in fsuffixes:
return 1
return 0
def _fortranEmitter(target, source, env):
node = source[0].rfile()
if not node.exists() and not node.is_derived():
print "Could not locate " + str(node.name)
return ([], [])
mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)"""
cre = re.compile(mod_regex,re.M)
# Retrieve all USE'd module names
modules = cre.findall(node.get_contents())
# Remove unique items from the list
modules = SCons.Util.unique(modules)
# Convert module name to a .mod filename
suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source)
moddir = env.subst('$FORTRANMODDIR', target=target, source=source)
modules = map(lambda x, s=suffix: string.lower(x) + s, modules)
for m in modules:
target.append(env.fs.File(m, moddir))
return (target, source)
def FortranEmitter(target, source, env):
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.StaticObjectEmitter(target, source, env)
def ShFortranEmitter(target, source, env):
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.SharedObjectEmitter(target, source, env)
def ComputeFortranSuffixes(suffixes, ppsuffixes):
"""suffixes are fortran source files, and ppsuffixes the ones to be
pre-processed. Both should be sequences, not strings."""
assert len(suffixes) > 0
s = suffixes[0]
sup = string.upper(s)
upper_suffixes = map(string.upper, suffixes)
if SCons.Util.case_sensitive_suffixes(s, sup):
ppsuffixes.extend(upper_suffixes)
else:
suffixes.extend(upper_suffixes)
def CreateDialectActions(dialect):
"""Create dialect specific actions."""
CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect)
CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect)
ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect)
ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect)
return CompAction, CompPPAction, ShCompAction, ShCompPPAction
def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0):
"""Add dialect specific construction variables."""
ComputeFortranSuffixes(suffixes, ppsuffixes)
fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect)
for suffix in suffixes + ppsuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan)
env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes)
compaction, compppaction, shcompaction, shcompppaction = \
CreateDialectActions(dialect)
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in suffixes:
static_obj.add_action(suffix, compaction)
shared_obj.add_action(suffix, shcompaction)
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
for suffix in ppsuffixes:
static_obj.add_action(suffix, compppaction)
shared_obj.add_action(suffix, shcompppaction)
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
if not env.has_key('%sFLAGS' % dialect):
env['%sFLAGS' % dialect] = SCons.Util.CLVar('')
if not env.has_key('SH%sFLAGS' % dialect):
env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
# If a tool does not define fortran prefix/suffix for include path, use C ones
if not env.has_key('INC%sPREFIX' % dialect):
env['INC%sPREFIX' % dialect] = '$INCPREFIX'
if not env.has_key('INC%sSUFFIX' % dialect):
env['INC%sSUFFIX' % dialect] = '$INCSUFFIX'
env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect)
if support_module == 1:
env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
else:
env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
def add_fortran_to_env(env):
"""Add Builders and construction variables for Fortran to an Environment."""
try:
FortranSuffixes = env['FORTRANFILESUFFIXES']
except KeyError:
FortranSuffixes = ['.f', '.for', '.ftn']
#print "Adding %s to fortran suffixes" % FortranSuffixes
try:
FortranPPSuffixes = env['FORTRANPPFILESUFFIXES']
except KeyError:
FortranPPSuffixes = ['.fpp', '.FPP']
DialectAddToEnv(env, "FORTRAN", FortranSuffixes,
FortranPPSuffixes, support_module = 1)
env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
env['FORTRANMODDIR'] = '' # where the compiler should place .mod files
env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
def add_f77_to_env(env):
"""Add Builders and construction variables for f77 to an Environment."""
try:
F77Suffixes = env['F77FILESUFFIXES']
except KeyError:
F77Suffixes = ['.f77']
#print "Adding %s to f77 suffixes" % F77Suffixes
try:
F77PPSuffixes = env['F77PPFILESUFFIXES']
except KeyError:
F77PPSuffixes = []
DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes)
def add_f90_to_env(env):
"""Add Builders and construction variables for f90 to an Environment."""
try:
F90Suffixes = env['F90FILESUFFIXES']
except KeyError:
F90Suffixes = ['.f90']
#print "Adding %s to f90 suffixes" % F90Suffixes
try:
F90PPSuffixes = env['F90PPFILESUFFIXES']
except KeyError:
F90PPSuffixes = []
DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes,
support_module = 1)
def add_f95_to_env(env):
"""Add Builders and construction variables for f95 to an Environment."""
try:
F95Suffixes = env['F95FILESUFFIXES']
except KeyError:
F95Suffixes = ['.f95']
#print "Adding %s to f95 suffixes" % F95Suffixes
try:
F95PPSuffixes = env['F95PPFILESUFFIXES']
except KeyError:
F95PPSuffixes = []
DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes,
support_module = 1)
def add_all_to_env(env):
"""Add builders and construction variables for all supported fortran
dialects."""
add_fortran_to_env(env)
add_f77_to_env(env)
add_f90_to_env(env)
add_f95_to_env(env)

View file

@ -5,7 +5,7 @@ Stuff for processing Java.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ Stuff for processing Java.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/JavaCommon.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/JavaCommon.py 3842 2008/12/20 22:59:52 scons"
import os import os
import os.path import os.path
@ -49,14 +49,16 @@ if java_parsing:
# double-backslashes; # double-backslashes;
# a single-line comment "//"; # a single-line comment "//";
# single or double quotes preceeded by a backslash; # single or double quotes preceeded by a backslash;
# single quotes, double quotes, open or close braces, semi-colons; # single quotes, double quotes, open or close braces, semi-colons,
# periods, open or close parentheses;
# floating-point numbers;
# any alphanumeric token (keyword, class name, specifier); # any alphanumeric token (keyword, class name, specifier);
# any alphanumeric token surrounded by angle brackets (generics);
# the multi-line comment begin and end tokens /* and */; # the multi-line comment begin and end tokens /* and */;
# array declarations "[]"; # array declarations "[]".
# semi-colons;
# periods.
_reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' +
r'[A-Za-z_][\w\$\.]*|/\*|\*/|\[\])') r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' +
r'/\*|\*/|\[\])')
class OuterState: class OuterState:
"""The initial state for parsing a Java file for classes, """The initial state for parsing a Java file for classes,
@ -73,6 +75,7 @@ if java_parsing:
self.stackBrackets = [] self.stackBrackets = []
self.brackets = 0 self.brackets = 0
self.nextAnon = 1 self.nextAnon = 1
self.localClasses = []
self.stackAnonClassBrackets = [] self.stackAnonClassBrackets = []
self.anonStacksStack = [[0]] self.anonStacksStack = [[0]]
self.package = None self.package = None
@ -124,6 +127,7 @@ if java_parsing:
if len(self.stackBrackets) and \ if len(self.stackBrackets) and \
self.brackets == self.stackBrackets[-1]: self.brackets == self.stackBrackets[-1]:
self.listOutputs.append(string.join(self.listClasses, '$')) self.listOutputs.append(string.join(self.listClasses, '$'))
self.localClasses.pop()
self.listClasses.pop() self.listClasses.pop()
self.anonStacksStack.pop() self.anonStacksStack.pop()
self.stackBrackets.pop() self.stackBrackets.pop()
@ -199,6 +203,8 @@ if java_parsing:
return IgnoreState('*/', self) return IgnoreState('*/', self)
elif token == '\n': elif token == '\n':
return self return self
elif token[0] == '<' and token[-1] == '>':
return self
elif token == '(': elif token == '(':
self.brace_level = self.brace_level + 1 self.brace_level = self.brace_level + 1
return self return self
@ -236,6 +242,20 @@ if java_parsing:
# the next non-whitespace token should be the name of the class # the next non-whitespace token should be the name of the class
if token == '\n': if token == '\n':
return self return self
# If that's an inner class which is declared in a method, it
# requires an index prepended to the class-name, e.g.
# 'Foo$1Inner' (Tigris Issue 2087)
if self.outer_state.localClasses and \
self.outer_state.stackBrackets[-1] > \
self.outer_state.stackBrackets[-2]+1:
locals = self.outer_state.localClasses[-1]
try:
idx = locals[token]
locals[token] = locals[token]+1
except KeyError:
locals[token] = 1
token = str(locals[token]) + token
self.outer_state.localClasses.append({})
self.outer_state.listClasses.append(token) self.outer_state.listClasses.append(token)
self.outer_state.anonStacksStack.append([0]) self.outer_state.anonStacksStack.append([0])
return self.outer_state return self.outer_state

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/Perforce.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/Perforce.py 3842 2008/12/20 22:59:52 scons"
import os import os

View file

@ -7,7 +7,7 @@ Phar Lap ETS tool chain. Right now, this is linkloc and
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -29,7 +29,7 @@ Phar Lap ETS tool chain. Right now, this is linkloc and
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/PharLapCommon.py 3842 2008/12/20 22:59:52 scons"
import os import os
import os.path import os.path

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/RCS.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/RCS.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Builder import SCons.Builder

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/SCCS.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/SCCS.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Builder import SCons.Builder

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/Subversion.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/Subversion.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path

View file

@ -14,7 +14,7 @@ tool definition.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -36,7 +36,7 @@ tool definition.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/__init__.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/__init__.py 3842 2008/12/20 22:59:52 scons"
import imp import imp
import sys import sys
@ -55,6 +55,7 @@ DefaultToolpath=[]
CScanner = SCons.Scanner.C.CScanner() CScanner = SCons.Scanner.C.CScanner()
DScanner = SCons.Scanner.D.DScanner() DScanner = SCons.Scanner.D.DScanner()
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
ProgramScanner = SCons.Scanner.Prog.ProgramScanner() ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
@ -76,8 +77,13 @@ for suffix in CSuffixes:
for suffix in DSuffixes: for suffix in DSuffixes:
SourceFileScanner.add_scanner(suffix, DScanner) SourceFileScanner.add_scanner(suffix, DScanner)
# FIXME: what should be done here? Two scanners scan the same extensions,
# but look for different files, e.g., "picture.eps" vs. "picture.pdf".
# The builders for DVI and PDF explicitly reference their scanners
# I think that means this is not needed???
for suffix in LaTeXSuffixes: for suffix in LaTeXSuffixes:
SourceFileScanner.add_scanner(suffix, LaTeXScanner) SourceFileScanner.add_scanner(suffix, LaTeXScanner)
SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
class Tool: class Tool:
def __init__(self, name, toolpath=[], **kw): def __init__(self, name, toolpath=[], **kw):
@ -136,7 +142,7 @@ class Tool:
file.close() file.close()
return module return module
except ImportError, e: except ImportError, e:
if e!="No module named %s"%self.name: if str(e)!="No module named %s"%self.name:
raise SCons.Errors.EnvironmentError, e raise SCons.Errors.EnvironmentError, e
try: try:
import zipimport import zipimport
@ -163,10 +169,10 @@ class Tool:
kw = self.init_kw kw = self.init_kw
env.Append(TOOLS = [ self.name ]) env.Append(TOOLS = [ self.name ])
if hasattr(self, 'options'): if hasattr(self, 'options'):
from SCons.Options import Options import SCons.Variables
if not env.has_key('options'): if not env.has_key('options'):
from SCons.Script import ARGUMENTS from SCons.Script import ARGUMENTS
env['options']=Options(args=ARGUMENTS) env['options']=SCons.Variables.Variables(args=ARGUMENTS)
opts=env['options'] opts=env['options']
self.options(opts) self.options(opts)
@ -422,32 +428,33 @@ def CreateJavaFileBuilder(env):
env['JAVASUFFIX'] = '.java' env['JAVASUFFIX'] = '.java'
return java_file return java_file
class ToolInitializer: class ToolInitializerMethod:
""" """
A class for delayed initialization of Tools modules. This is added to a construction environment in place of a
method(s) normally called for a Builder (env.Object, env.StaticObject,
This is intended to be added to a construction environment in etc.). When called, it has its associated ToolInitializer
place of the method(s) normally called for a Builder (env.Object, object search the specified list of tools and apply the first
env.StaticObject, etc.). When called, it searches the specified one that exists to the construction environment. It then calls
list of tools, applies the first one that exists to the construction whatever builder was (presumably) added to the construction
environment, and calls whatever builder was (presumably) added the environment in place of this particular instance.
construction environment in our place.
""" """
def __init__(self, name, tools): def __init__(self, name, initializer):
""" """
Note: we store the tool name as __name__ so it can be used by Note: we store the tool name as __name__ so it can be used by
the class that attaches this to a construction environment. the class that attaches this to a construction environment.
""" """
self.__name__ = name self.__name__ = name
if not SCons.Util.is_List(tools): self.initializer = initializer
tools = [tools]
self.tools = tools def get_builder(self, env):
def __call__(self, env, *args, **kw): """
for t in self.tools: Returns the appropriate real Builder for this method name
tool = SCons.Tool.Tool(t) after having the associated ToolInitializer object apply
if tool.exists(env): the appropriate Tool module.
env.Tool(tool) """
break builder = getattr(env, self.__name__)
self.initializer.apply_tools(env)
builder = getattr(env, self.__name__) builder = getattr(env, self.__name__)
if builder is self: if builder is self:
@ -455,21 +462,79 @@ class ToolInitializer:
# for this name was found (or possibly there's a mismatch # for this name was found (or possibly there's a mismatch
# between the name we were called by and the Builder name # between the name we were called by and the Builder name
# added by the Tool module). # added by the Tool module).
# return None
# (Eventually this is where we'll put a more informative
# error message about the inability to find that tool
# as cut over more Builders+Tools to using this.
return [], []
# Let the construction environment remove the added method self.initializer.remove_methods(env)
# so we no longer copy and re-bind this method when the
# construction environment gets cloned. return builder
env.RemoveMethod(self)
def __call__(self, env, *args, **kw):
"""
"""
builder = self.get_builder(env)
if builder is None:
return [], []
return apply(builder, args, kw) return apply(builder, args, kw)
class ToolInitializer:
"""
A class for delayed initialization of Tools modules.
Instances of this class associate a list of Tool modules with
a list of Builder method names that will be added by those Tool
modules. As part of instantiating this object for a particular
construction environment, we also add the appropriate
ToolInitializerMethod objects for the various Builder methods
that we want to use to delay Tool searches until necessary.
"""
def __init__(self, env, tools, names):
if not SCons.Util.is_List(tools):
tools = [tools]
if not SCons.Util.is_List(names):
names = [names]
self.env = env
self.tools = tools
self.names = names
self.methods = {}
for name in names:
method = ToolInitializerMethod(name, self)
self.methods[name] = method
env.AddMethod(method)
def remove_methods(self, env):
"""
Removes the methods that were added by the tool initialization
so we no longer copy and re-bind them when the construction
environment gets cloned.
"""
for method in self.methods.values():
env.RemoveMethod(method)
def apply_tools(self, env):
"""
Searches the list of associated Tool modules for one that
exists, and applies that to the construction environment.
"""
for t in self.tools:
tool = SCons.Tool.Tool(t)
if tool.exists(env):
env.Tool(tool)
return
# If we fall through here, there was no tool module found.
# This is where we can put an informative error message
# about the inability to find the tool. We'll start doing
# this as we cut over more pre-defined Builder+Tools to use
# the ToolInitializer class.
def Initializers(env): def Initializers(env):
env.AddMethod(ToolInitializer('Install', 'install')) ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs'])
env.AddMethod(ToolInitializer('InstallAs', 'install')) def Install(self, *args, **kw):
return apply(self._InternalInstall, args, kw)
def InstallAs(self, *args, **kw):
return apply(self._InternalInstallAs, args, kw)
env.AddMethod(Install)
env.AddMethod(InstallAs)
def FindTool(tools, env): def FindTool(tools, env):
for tool in tools: for tool in tools:
@ -496,7 +561,7 @@ def tool_list(platform, env):
c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
assemblers = ['masm', 'nasm', 'gas', '386asm' ] assemblers = ['masm', 'nasm', 'gas', '386asm' ]
fortran_compilers = ['g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
ars = ['mslib', 'ar', 'tlib'] ars = ['mslib', 'ar', 'tlib']
elif str(platform) == 'os2': elif str(platform) == 'os2':
"prefer IBM tools on OS/2" "prefer IBM tools on OS/2"
@ -520,7 +585,8 @@ def tool_list(platform, env):
c_compilers = ['suncc', 'gcc', 'cc'] c_compilers = ['suncc', 'gcc', 'cc']
cxx_compilers = ['sunc++', 'g++', 'c++'] cxx_compilers = ['sunc++', 'g++', 'c++']
assemblers = ['as', 'gas'] assemblers = ['as', 'gas']
fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
'gfortran', 'g77', 'fortran']
ars = ['sunar'] ars = ['sunar']
elif str(platform) == 'hpux': elif str(platform) == 'hpux':
"prefer aCC tools on HP-UX" "prefer aCC tools on HP-UX"
@ -544,7 +610,7 @@ def tool_list(platform, env):
c_compilers = ['gcc', 'cc'] c_compilers = ['gcc', 'cc']
cxx_compilers = ['g++', 'c++'] cxx_compilers = ['g++', 'c++']
assemblers = ['as'] assemblers = ['as']
fortran_compilers = ['f95', 'f90', 'g77'] fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
ars = ['ar'] ars = ['ar']
else: else:
"prefer GNU tools on all other platforms" "prefer GNU tools on all other platforms"
@ -552,7 +618,7 @@ def tool_list(platform, env):
c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
assemblers = ['gas', 'nasm', 'masm'] assemblers = ['gas', 'nasm', 'masm']
fortran_compilers = ['f95', 'f90', 'g77', 'ifort', 'ifl', 'fortran'] fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
ars = ['ar', 'mslib'] ars = ['ar', 'mslib']
c_compiler = FindTool(c_compilers, env) or c_compilers[0] c_compiler = FindTool(c_compilers, env) or c_compilers[0]

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/aixc++.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/aixc++.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/aixcc.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/aixcc.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/aixf77.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/aixf77.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/aixlink.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/aixlink.py 3842 2008/12/20 22:59:52 scons"
import os import os
import os.path import os.path

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,23 +31,25 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/applelink.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/applelink.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util import SCons.Util
import gnulink # Even though the Mac is based on the GNU toolchain, it doesn't understand
# the -rpath option, so we use the "link" tool instead of "gnulink".
import link
def generate(env): def generate(env):
"""Add Builders and construction variables for applelink to an """Add Builders and construction variables for applelink to an
Environment.""" Environment."""
gnulink.generate(env) link.generate(env)
env['FRAMEWORKPATHPREFIX'] = '-F' env['FRAMEWORKPATHPREFIX'] = '-F'
env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}'
env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}'
env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS' env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib')
env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS' env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
# override the default for loadable modules, which are different # override the default for loadable modules, which are different
# on OS X than dynamic shared libs. echoing what XCode does for # on OS X than dynamic shared libs. echoing what XCode does for
@ -60,5 +62,4 @@ def generate(env):
def exists(env): def exists(env):
import sys return env['PLATFORM'] == 'darwin'
return sys.platform == 'darwin'

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/ar.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/ar.py 3842 2008/12/20 22:59:52 scons"
import SCons.Defaults import SCons.Defaults
import SCons.Tool import SCons.Tool

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/as.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/as.py 3842 2008/12/20 22:59:52 scons"
import SCons.Defaults import SCons.Defaults
import SCons.Tool import SCons.Tool
@ -40,7 +40,7 @@ import SCons.Util
assemblers = ['as'] assemblers = ['as']
ASSuffixes = ['.s', '.asm', '.ASM'] ASSuffixes = ['.s', '.asm', '.ASM']
ASPPSuffixes = ['.spp', '.SPP'] ASPPSuffixes = ['.spp', '.SPP', '.sx']
if SCons.Util.case_sensitive_suffixes('.s', '.S'): if SCons.Util.case_sensitive_suffixes('.s', '.S'):
ASPPSuffixes.extend(['.S']) ASPPSuffixes.extend(['.S'])
else: else:

View file

@ -5,7 +5,7 @@ XXX
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ XXX
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/bcc32.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/bcc32.py 3842 2008/12/20 22:59:52 scons"
import os import os
import os.path import os.path

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/c++.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/c++.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path
@ -73,11 +73,11 @@ def generate(env):
SCons.Tool.cc.add_common_cc_variables(env) SCons.Tool.cc.add_common_cc_variables(env)
env['CXX'] = 'c++' env['CXX'] = 'c++'
env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['CXXFLAGS'] = SCons.Util.CLVar('')
env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $_CCCOMCOM $SOURCES' env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
env['SHCXX'] = '$CXX' env['SHCXX'] = '$CXX'
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $_CCCOMCOM $SOURCES' env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
env['CPPDEFPREFIX'] = '-D' env['CPPDEFPREFIX'] = '-D'
env['CPPDEFSUFFIX'] = '' env['CPPDEFSUFFIX'] = ''

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/cc.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/cc.py 3842 2008/12/20 22:59:52 scons"
import SCons.Tool import SCons.Tool
import SCons.Defaults import SCons.Defaults

View file

@ -5,7 +5,7 @@ Tool-specific initialization for the Compaq Visual Fortran compiler.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ Tool-specific initialization for the Compaq Visual Fortran compiler.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/cvf.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/cvf.py 3842 2008/12/20 22:59:52 scons"
import fortran import fortran

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/default.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/default.py 3842 2008/12/20 22:59:52 scons"
import SCons.Tool import SCons.Tool

View file

@ -14,14 +14,15 @@ use absolute paths. To hack around it, add '#/blah'. This will link
blah.lib from the directory where SConstruct resides. blah.lib from the directory where SConstruct resides.
Compiler variables: Compiler variables:
DC - The name of the D compiler to use. Defaults to dmd. DC - The name of the D compiler to use. Defaults to dmd or gdmd,
whichever is found.
DPATH - List of paths to search for import modules. DPATH - List of paths to search for import modules.
DVERSIONS - List of version tags to enable when compiling. DVERSIONS - List of version tags to enable when compiling.
DDEBUG - List of debug tags to enable when compiling. DDEBUG - List of debug tags to enable when compiling.
Linker related variables: Linker related variables:
LIBS - List of library files to link in. LIBS - List of library files to link in.
DLINK - Name of the linker to use. Defaults to dmd. DLINK - Name of the linker to use. Defaults to dmd or gdmd.
DLINKFLAGS - List of linker flags. DLINKFLAGS - List of linker flags.
Lib tool variables: Lib tool variables:
@ -31,7 +32,7 @@ Lib tool variables:
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -53,7 +54,7 @@ Lib tool variables:
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/dmd.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/dmd.py 3842 2008/12/20 22:59:52 scons"
import os import os
import string import string
@ -93,7 +94,8 @@ def generate(env):
static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter)
shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter)
env['DC'] = 'dmd' dc = env.Detect(['dmd', 'gdmd'])
env['DC'] = dc
env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES'
env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)'
@ -105,11 +107,12 @@ def generate(env):
env['DVERSIONS'] = [] env['DVERSIONS'] = []
env['DDEBUG'] = [] env['DDEBUG'] = []
if dc:
# Add the path to the standard library. # Add the path to the standard library.
# This is merely for the convenience of the dependency scanner. # This is merely for the convenience of the dependency scanner.
dmd_path = env.WhereIs('dmd') dmd_path = env.WhereIs(dc)
if dmd_path: if dmd_path:
x = string.rindex(dmd_path, 'dmd') x = string.rindex(dmd_path, dc)
phobosDir = dmd_path[:x] + '/../src/phobos' phobosDir = dmd_path[:x] + '/../src/phobos'
if os.path.isdir(phobosDir): if os.path.isdir(phobosDir):
env.Append(DPATH = [phobosDir]) env.Append(DPATH = [phobosDir])
@ -191,14 +194,17 @@ def generate(env):
env['SMART_LINKCOM'] = smart_link[linkcom] env['SMART_LINKCOM'] = smart_link[linkcom]
except KeyError: except KeyError:
def _smartLink(source, target, env, for_signature, def _smartLink(source, target, env, for_signature,
defaultLinker=linkcom): defaultLinker=linkcom, dc=dc):
if isD(source): if isD(source):
try: try:
libs = env['LIBS'] libs = env['LIBS']
except KeyError: except KeyError:
libs = [] libs = []
if 'phobos' not in libs: if 'phobos' not in libs:
if dc is 'dmd':
env.Append(LIBS = ['phobos']) env.Append(LIBS = ['phobos'])
elif dc is 'gdmd':
env.Append(LIBS = ['gphobos'])
if 'pthread' not in libs: if 'pthread' not in libs:
env.Append(LIBS = ['pthread']) env.Append(LIBS = ['pthread'])
if 'm' not in libs: if 'm' not in libs:
@ -209,4 +215,4 @@ def generate(env):
env['LINKCOM'] = '$SMART_LINKCOM ' env['LINKCOM'] = '$SMART_LINKCOM '
def exists(env): def exists(env):
return env.Detect('dmd') return env.Detect(['dmd', 'gdmd'])

View file

@ -5,7 +5,7 @@ Common DVI Builder definition for various other Tool modules that use it.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -27,7 +27,7 @@ Common DVI Builder definition for various other Tool modules that use it.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/dvi.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/dvi.py 3842 2008/12/20 22:59:52 scons"
import SCons.Builder import SCons.Builder
import SCons.Tool import SCons.Tool

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,14 +31,54 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/dvipdf.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/dvipdf.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Defaults import SCons.Defaults
import SCons.Tool.pdf import SCons.Tool.pdf
import SCons.Tool.tex
import SCons.Util import SCons.Util
_null = SCons.Scanner.LaTeX._null
def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None):
"""A builder for DVI files that sets the TEXPICTS environment
variable before running dvi2ps or dvipdf."""
try:
abspath = source[0].attributes.path
except AttributeError :
abspath = ''
saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath)
result = XXXDviAction(target, source, env)
if saved_env is _null:
try:
del env['ENV']['TEXPICTS']
except KeyError:
pass # was never set
else:
env['ENV']['TEXPICTS'] = saved_env
return result
def DviPdfFunction(target = None, source= None, env=None):
result = DviPdfPsFunction(PDFAction,target,source,env)
return result
def DviPdfStrFunction(target = None, source= None, env=None):
"""A strfunction for dvipdf that returns the appropriate
command string for the no_exec options."""
if env.GetOption("no_exec"):
result = env.subst('$DVIPDFCOM',0,target,source)
else:
result = ''
return result
PDFAction = None PDFAction = None
DVIPDFAction = None
def PDFEmitter(target, source, env): def PDFEmitter(target, source, env):
"""Strips any .aux or .log files from the input source list. """Strips any .aux or .log files from the input source list.
@ -57,11 +97,15 @@ def generate(env):
if PDFAction is None: if PDFAction is None:
PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR')
global DVIPDFAction
if DVIPDFAction is None:
DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction)
import pdf import pdf
pdf.generate(env) pdf.generate(env)
bld = env['BUILDERS']['PDF'] bld = env['BUILDERS']['PDF']
bld.add_action('.dvi', PDFAction) bld.add_action('.dvi', DVIPDFAction)
bld.add_emitter('.dvi', PDFEmitter) bld.add_emitter('.dvi', PDFEmitter)
env['DVIPDF'] = 'dvipdf' env['DVIPDF'] = 'dvipdf'

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,13 +31,28 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/dvips.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/dvips.py 3842 2008/12/20 22:59:52 scons"
import SCons.Action import SCons.Action
import SCons.Builder import SCons.Builder
import SCons.Tool.dvipdf
import SCons.Util import SCons.Util
def DviPsFunction(target = None, source= None, env=None):
result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env)
return result
def DviPsStrFunction(target = None, source= None, env=None):
"""A strfunction for dvipdf that returns the appropriate
command string for the no_exec options."""
if env.GetOption("no_exec"):
result = env.subst('$PSCOM',0,target,source)
else:
result = ''
return result
PSAction = None PSAction = None
DVIPSAction = None
PSBuilder = None PSBuilder = None
def generate(env): def generate(env):
@ -46,19 +61,24 @@ def generate(env):
if PSAction is None: if PSAction is None:
PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR')
global DVIPSAction
if DVIPSAction is None:
DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction)
global PSBuilder global PSBuilder
if PSBuilder is None: if PSBuilder is None:
PSBuilder = SCons.Builder.Builder(action = PSAction, PSBuilder = SCons.Builder.Builder(action = PSAction,
prefix = '$PSPREFIX', prefix = '$PSPREFIX',
suffix = '$PSSUFFIX', suffix = '$PSSUFFIX',
src_suffix = '.dvi', src_suffix = '.dvi',
src_builder = 'DVI') src_builder = 'DVI',
single_source=True)
env['BUILDERS']['PostScript'] = PSBuilder env['BUILDERS']['PostScript'] = PSBuilder
env['DVIPS'] = 'dvips' env['DVIPS'] = 'dvips'
env['DVIPSFLAGS'] = SCons.Util.CLVar('') env['DVIPSFLAGS'] = SCons.Util.CLVar('')
# I'm not quite sure I got the directories and filenames right for build_dir # I'm not quite sure I got the directories and filenames right for variant_dir
# We need to be in the correct directory for the sake of latex \includegraphics eps included files. # We need to be in the correct directory for the sake of latex \includegraphics eps included files.
env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}'
env['PSPREFIX'] = '' env['PSPREFIX'] = ''

View file

@ -0,0 +1,56 @@
"""engine.SCons.Tool.f77
Tool-specific initialization for the generic Posix f77 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/f77.py 3842 2008/12/20 22:59:52 scons"
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env
compilers = ['f77']
def generate(env):
add_all_to_env(env)
add_f77_to_env(env)
fcomp = env.Detect(compilers) or 'f77'
env['F77'] = fcomp
env['SHF77'] = fcomp
env['FORTRAN'] = fcomp
env['SHFORTRAN'] = fcomp
def exists(env):
return env.Detect(compilers)

View file

@ -1,6 +1,6 @@
"""engine.SCons.Tool.g77 """engine.SCons.Tool.f90
Tool-specific initialization for g77. Tool-specific initialization for the generic Posix f90 Fortran compiler.
There normally shouldn't be any need to import this module directly. There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool() It will usually be imported through the generic SCons.Tool.Tool()
@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,17 +31,26 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/g77.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/f90.py 3842 2008/12/20 22:59:52 scons"
import f77 import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env
compilers = ['g77', 'f77'] compilers = ['f90']
def generate(env): def generate(env):
"""Add Builders and construction variables for g77 to an Environment.""" add_all_to_env(env)
f77.generate(env) add_f90_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'g77' fc = env.Detect(compilers) or 'f90'
env['F90'] = fc
env['SHF90'] = fc
env['FORTRAN'] = fc
env['SHFORTRAN'] = fc
def exists(env): def exists(env):
return env.Detect(compilers) return env.Detect(compilers)

View file

@ -0,0 +1,57 @@
"""engine.SCons.Tool.f95
Tool-specific initialization for the generic Posix f95 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/f95.py 3842 2008/12/20 22:59:52 scons"
import SCons.Defaults
import SCons.Tool
import SCons.Util
import fortran
from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env
compilers = ['f95']
def generate(env):
add_all_to_env(env)
add_f95_to_env(env)
fcomp = env.Detect(compilers) or 'f95'
env['F95'] = fcomp
env['SHF95'] = fcomp
env['FORTRAN'] = fcomp
env['SHFORTRAN'] = fcomp
def exists(env):
return env.Detect(compilers)

View file

@ -8,7 +8,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -30,7 +30,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/filesystem.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/filesystem.py 3842 2008/12/20 22:59:52 scons"
import SCons import SCons
from SCons.Tool.install import copyFunc from SCons.Tool.install import copyFunc

View file

@ -0,0 +1,57 @@
"""SCons.Tool.fortran
Tool-specific initialization for a generic Posix f77/f90 Fortran compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/fortran.py 3842 2008/12/20 22:59:52 scons"
import re
import string
import SCons.Action
import SCons.Defaults
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env
compilers = ['f95', 'f90', 'f77']
def generate(env):
add_all_to_env(env)
add_fortran_to_env(env)
fc = env.Detect(compilers) or 'f77'
env['SHFORTRAN'] = fc
env['FORTRAN'] = fc
def exists(env):
return env.Detect(compilers)

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,12 +31,12 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/g++.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/g++.py 3842 2008/12/20 22:59:52 scons"
import os.path import os.path
import re import re
import subprocess
import SCons.Defaults
import SCons.Tool import SCons.Tool
import SCons.Util import SCons.Util
@ -53,35 +53,32 @@ def generate(env):
env['CXX'] = env.Detect(compilers) env['CXX'] = env.Detect(compilers)
# platform specific settings # platform specific settings
if env['PLATFORM'] == 'cygwin': if env['PLATFORM'] == 'aix':
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
elif env['PLATFORM'] == 'aix':
# Original line from Christian Engel added -DPIC:
#env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC -mminimal-toc')
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc')
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
env['SHOBJSUFFIX'] = '$OBJSUFFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX'
elif env['PLATFORM'] == 'hpux': elif env['PLATFORM'] == 'hpux':
# Original line from Christian Engel added -DPIC:
#env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
env['SHOBJSUFFIX'] = '.pic.o' env['SHOBJSUFFIX'] = '.pic.o'
elif env['PLATFORM'] == 'sunos': elif env['PLATFORM'] == 'sunos':
# Original line from Christian Engel added -DPIC:
#env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
env['SHOBJSUFFIX'] = '.pic.o' env['SHOBJSUFFIX'] = '.pic.o'
else:
# Original line from Christian Engel added -DPIC:
#env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
# determine compiler version # determine compiler version
if env['CXX']: if env['CXX']:
line = os.popen(env['CXX'] + ' --version').readline() #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'],
pipe = SCons.Action._subproc(env, [env['CXX'], '--version'],
stdin = 'devnull',
stderr = 'devnull',
stdout = subprocess.PIPE)
if pipe.wait() != 0: return
# -dumpversion was added in GCC 3.0. As long as we're supporting
# GCC versions older than that, we should use --version and a
# regular expression.
#line = pipe.stdout.read().strip()
#if line:
# env['CXXVERSION'] = line
line = pipe.stdout.readline()
match = re.search(r'[0-9]+(\.[0-9]+)+', line) match = re.search(r'[0-9]+(\.[0-9]+)+', line)
if match: if match:
env['CXXVERSION'] = match.group(0) env['CXXVERSION'] = match.group(0)
def exists(env): def exists(env):
return env.Detect(compilers) return env.Detect(compilers)

View file

@ -0,0 +1,67 @@
"""engine.SCons.Tool.g77
Tool-specific initialization for g77.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/g77.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util
from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env
compilers = ['g77', 'f77']
def generate(env):
"""Add Builders and construction variables for g77 to an Environment."""
add_all_to_env(env)
add_f77_to_env(env)
fcomp = env.Detect(compilers) or 'g77'
if env['PLATFORM'] in ['cygwin', 'win32']:
env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS')
env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS')
else:
env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC')
env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC')
env['FORTRAN'] = fcomp
env['SHFORTRAN'] = '$FORTRAN'
env['F77'] = fcomp
env['SHF77'] = '$F77'
env['INCFORTRANPREFIX'] = "-I"
env['INCFORTRANSUFFIX'] = ""
env['INCF77PREFIX'] = "-I"
env['INCF77SUFFIX'] = ""
def exists(env):
return env.Detect(compilers)

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/gas.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/gas.py 3842 2008/12/20 22:59:52 scons"
as_module = __import__('as', globals(), locals(), []) as_module = __import__('as', globals(), locals(), [])

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,13 +31,14 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/gcc.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/gcc.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util
import cc import cc
import os import os
import re import re
import subprocess
import SCons.Util
compilers = ['gcc', 'cc'] compilers = ['gcc', 'cc']
@ -52,7 +53,19 @@ def generate(env):
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC')
# determine compiler version # determine compiler version
if env['CC']: if env['CC']:
line = os.popen(env['CC'] + ' --version').readline() #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'],
pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
stdin = 'devnull',
stderr = 'devnull',
stdout = subprocess.PIPE)
if pipe.wait() != 0: return
# -dumpversion was added in GCC 3.0. As long as we're supporting
# GCC versions older than that, we should use --version and a
# regular expression.
#line = pipe.stdout.read().strip()
#if line:
# env['CCVERSION'] = line
line = pipe.stdout.readline()
match = re.search(r'[0-9]+(\.[0-9]+)+', line) match = re.search(r'[0-9]+(\.[0-9]+)+', line)
if match: if match:
env['CCVERSION'] = match.group(0) env['CCVERSION'] = match.group(0)

View file

@ -0,0 +1,58 @@
"""SCons.Tool.gfortran
Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran
2003 compiler.
There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
#
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
#
# 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__ = "src/engine/SCons/Tool/gfortran.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util
import fortran
def generate(env):
"""Add Builders and construction variables for gfortran to an
Environment."""
fortran.generate(env)
for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
env['%s' % dialect] = 'gfortran'
env['SH%s' % dialect] = '$%s' % dialect
if env['PLATFORM'] in ['cygwin', 'win32']:
env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
else:
env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect)
env['INC%sPREFIX' % dialect] = "-I"
env['INC%sSUFFIX' % dialect] = ""
def exists(env):
return env.Detect('gfortran')

View file

@ -9,7 +9,7 @@ selection method.
""" """
# #
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,7 +31,7 @@ selection method.
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
__revision__ = "src/engine/SCons/Tool/gnulink.py 2523 2007/12/12 09:37:41 knight" __revision__ = "src/engine/SCons/Tool/gnulink.py 3842 2008/12/20 22:59:52 scons"
import SCons.Util import SCons.Util

Some files were not shown because too many files have changed in this diff Show more