multi-stage builder.
.IP emitter
-A function that is passed the target, source, and environment,
-and which returns a tuple containing two lists,
-the list of targets to be built by this builder,
-and the list of sources for this builder.
-This allows the target and source lists to
-be manipulated before the target(s) are actually built.
-
-The emitter function
+A function to manipulate the target and source
+lists before dependencies are established
+and the target(s) are actually built.
+.B emitter
+can also be string containing a construction variable to expand
+to an emitter function,
+or a dictionary mapping source file suffixes
+to emitter functions.
+(Only the suffix of the first source file
+is used to select the actual emitter function
+from an emitter dictionary.)
+
+An emitter function
takes three arguments:
.I source
- a list of source nodes,
- a list of target nodes,
.I env
- the construction environment.
+An emitter must return a tuple containing two lists,
+the list of targets to be built by this builder,
+and the list of sources for this builder.
Example:
def e(target, source, env):
return (target + ['foo.foo'], source + ['foo.src'])
-b = Builder(emitter=e)
+# Simple association of an emitter function with a Builder.
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = e)
+
+# Calling an emitter through a construction variable.
+env = Environment(MY_EMITTER = e)
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = '$MY_EMITTER')
+
+# Associating multiple emitters with different file
+# suffixes using a dictionary.
+def e_suf1(target, source, env):
+ return (target + ['another_target_file'], source)
+def e_suf2(target, source, env):
+ return (target, source + ['another_source_file'])
+b = Builder("my_build < $TARGET > $SOURCE",
+ emitter = {'.suf1' : e_suf1,
+ '.suf2' : e_suf2})
.EE
.IP generator
- Correct the spelling of the "validater" option to "validator."
Add a DeprecatedWarning when the old spelling is used.
+ - Allow a Builder's emitter to be a dictionary that maps source file
+ suffixes to emitter functions, using the suffix of the first file
+ in the source list to pick the right one.
+
+ - Refactor the creation of the Program, *Object and *Library Builders
+ so that they're moved out of SCons.Defaults and created on demand.
+
From Gary Oberbrunner:
- Report the target being built in error messages when building
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
-
import os.path
-from SCons.Errors import InternalError, UserError
+import UserDict
import SCons.Action
+from SCons.Errors import InternalError, UserError
import SCons.Executor
import SCons.Node
import SCons.Node.FS
def __cmp__(self, other):
return cmp(self.action_dict, other.action_dict)
+class DictEmitter(UserDict.UserDict):
+ """A callable dictionary that maps file suffixes to emitters.
+ When called, it finds the right emitter in its dictionary for the
+ suffix of the first source file, and calls that emitter to get the
+ right lists of targets and sources to return. If there's no emitter
+ for the suffix in its dictionary, the original target and source are
+ returned.
+ """
+ def __call__(self, target, source, env):
+ ext = SCons.Util.splitext(str(source[0]))[1]
+ if ext:
+ try:
+ emitter = self[ext]
+ except KeyError:
+ # Before raising the user error, try to perform Environment
+ # substitution on the keys of emitter_dict.
+ s_dict = {}
+ for (k,v) in self.items():
+ s_k = env.subst(k)
+ s_dict[s_k] = v
+ try:
+ emitter = s_dict[ext]
+ except KeyError:
+ emitter = None
+ if emitter:
+ target, source = emitter(target, source, env)
+ return (target, source)
+
def Builder(**kw):
"""A factory for builder objects."""
composite = None
kw['action'] = SCons.Action.CommandGenerator(composite)
kw['src_suffix'] = composite.src_suffixes()
- if kw.has_key('emitter') and \
- SCons.Util.is_String(kw['emitter']):
- # This allows users to pass in an Environment
- # variable reference (like "$FOO") as an emitter.
- # We will look in that Environment variable for
- # a callable to use as the actual emitter.
- var = SCons.Util.get_environment_var(kw['emitter'])
- if not var:
- raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % kw['emitter']
- kw['emitter'] = EmitterProxy(var)
+ if kw.has_key('emitter'):
+ emitter = kw['emitter']
+ if SCons.Util.is_String(emitter):
+ # This allows users to pass in an Environment
+ # variable reference (like "$FOO") as an emitter.
+ # We will look in that Environment variable for
+ # a callable to use as the actual emitter.
+ var = SCons.Util.get_environment_var(emitter)
+ if not var:
+ raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
+ kw['emitter'] = EmitterProxy(var)
+ elif SCons.Util.is_Dict(emitter):
+ kw['emitter'] = DictEmitter(emitter)
if kw.has_key('src_builder'):
ret = apply(MultiStepBuilder, (), kw)
"""
return [ node ]
+ def add_emitter(self, suffix, emitter):
+ """Add a suffix-emitter mapping to this Builder.
+
+ This assumes that emitter has been initialized with an
+ appropriate dictionary type, and will throw a TypeError if
+ not, so the caller is responsible for knowing that this is an
+ appropriate method to call for the Builder in question.
+ """
+ self.emitter[suffix] = emitter
+
class ListBuilder(SCons.Util.Proxy):
"""A Proxy to support building an array of targets (for example,
foo.o and foo.h from foo.y) from a single Action execution.
assert tgt.builder is builder3, tgt.builder
assert node.builder is new_builder, node.builder
+ # Test use of a dictionary mapping file suffixes to
+ # emitter functions
+ def emit4a(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4a-' + x[:-3], source)
+ return (target, source)
+ def emit4b(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4b-' + x[:-3], source)
+ return (target, source)
+ builder4 = SCons.Builder.Builder(action='foo',
+ emitter={'.4a':emit4a,
+ '.4b':emit4b},
+ node_factory=MyNode)
+ tgt = builder4(env, source='aaa.4a')
+ assert str(tgt) == 'emit4a-aaa', str(tgt)
+ tgt = builder4(env, source='bbb.4b')
+ assert str(tgt) == 'emit4b-bbb', str(tgt)
+ tgt = builder4(env, source='ccc.4c')
+ assert str(tgt) == 'ccc', str(tgt)
+
+ def emit4c(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4c-' + x[:-3], source)
+ return (target, source)
+ builder4.add_emitter('.4c', emit4c)
+ tgt = builder4(env, source='ccc.4c')
+ assert str(tgt) == 'emit4c-ccc', str(tgt)
+
def test_no_target(self):
"""Test deducing the target from the source."""
FortranScan = SCons.Scanner.Fortran.FortranScan()
-def yaccEmitter(target, source, env, **kw):
- # If -d is specified on the command line, yacc will emit a .h
- # or .hpp file as well as a .c or .cpp file, depending on whether
- # the input file is a .y or .yy, respectively.
- if len(source) and '-d' in string.split(env.subst("$YACCFLAGS")):
- suff = os.path.splitext(SCons.Util.to_String(source[0]))[1]
- h = None
- if suff == '.y':
- h = '.h'
- elif suff == '.yy':
- h = '.hpp'
- if h:
- base = os.path.splitext(SCons.Util.to_String(target[0]))[0]
- target.append(base + h)
- return (target, source)
-
-def CFile():
- """Common function to generate a C file Builder."""
- return SCons.Builder.Builder(action = {},
- emitter = yaccEmitter,
- suffix = '$CFILESUFFIX')
-
-def CXXFile():
- """Common function to generate a C++ file Builder."""
- return SCons.Builder.Builder(action = {},
- emitter = yaccEmitter,
- suffix = '$CXXFILESUFFIX')
-
class SharedFlagChecker:
"""This is a callable class that is used as
a build action for all objects, libraries, and programs.
ASAction = SCons.Action.Action([ StaticCheckSet, "$ASCOM" ])
ASPPAction = SCons.Action.Action([ StaticCheckSet, "$ASPPCOM" ])
-
-def StaticObject():
- """A function for generating the static object Builder."""
- return SCons.Builder.Builder(action = {},
- emitter="$OBJEMITTER",
- prefix = '$OBJPREFIX',
- suffix = '$OBJSUFFIX',
- src_builder = ['CFile', 'CXXFile'])
-
-def SharedObject():
- """A function for generating the shared object Builder."""
- return SCons.Builder.Builder(action = {},
- prefix = '$SHOBJPREFIX',
- suffix = '$SHOBJSUFFIX',
- emitter="$OBJEMITTER",
- src_builder = ['CFile', 'CXXFile'])
-
ProgScan = SCons.Scanner.Prog.ProgScan()
-StaticLibrary = SCons.Builder.Builder(action=[ StaticCheck, "$ARCOM" ],
- emitter='$LIBEMITTER',
- prefix = '$LIBPREFIX',
- suffix = '$LIBSUFFIX',
- src_suffix = '$OBJSUFFIX',
- src_builder = 'Object')
-
-SharedLibrary = SCons.Builder.Builder(action=[ SharedCheck, "$SHLINKCOM" ],
- emitter="$SHLIBEMITTER",
- prefix = '$SHLIBPREFIX',
- suffix = '$SHLIBSUFFIX',
- scanner = ProgScan,
- src_suffix = '$SHOBJSUFFIX',
- src_builder = 'SharedObject')
-
def DVI():
"""Common function to generate a DVI file Builder."""
return SCons.Builder.Builder(action = {},
prefix = '$PDFPREFIX',
suffix = '$PDFSUFFIX')
-Program = SCons.Builder.Builder(action=[ StaticCheck, '$LINKCOM' ],
- emitter='$PROGEMITTER',
- prefix='$PROGPREFIX',
- suffix='$PROGSUFFIX',
- src_suffix='$OBJSUFFIX',
- src_builder='Object',
- scanner = ProgScan)
-
def copyFunc(dest, source, env):
"""Install a source file into a destination by copying it (and its
permission/mode bits)."""
return self.cmd
ConstructionEnvironment = {
- 'BUILDERS' : { 'SharedLibrary' : SharedLibrary,
- 'Library' : StaticLibrary,
- 'StaticLibrary' : StaticLibrary,
- 'Alias' : Alias,
- 'Program' : Program },
+ 'BUILDERS' : { 'Alias' : Alias },
'SCANNERS' : [CScan, FortranScan],
'PDFPREFIX' : '',
'PDFSUFFIX' : '.pdf',
globals['GetOption'] = GetOption
globals['Help'] = Help
globals['Import'] = Import
- globals['Library'] = SCons.Defaults.StaticLibrary
globals['Literal'] = SCons.Util.Literal
globals['Local'] = Local
- globals['Object'] = SCons.Defaults.StaticObject
globals['Options'] = Options
globals['ParseConfig'] = SCons.Util.ParseConfig
globals['Platform'] = SCons.Platform.Platform
- globals['Program'] = SCons.Defaults.Program
globals['Repository'] = SCons.Node.FS.default_fs.Repository
globals['Return'] = Return
globals['SConscript'] = SConscript
globals['SetContentSignatureType'] = SetContentSignatureType
globals['SetJobs'] = SetJobs
globals['SetOption'] = SetOption
- globals['SharedLibrary'] = SCons.Defaults.SharedLibrary
- globals['SharedObject'] = SCons.Defaults.SharedObject
globals['SourceSignatures'] = SourceSignatures
globals['Split'] = SCons.Util.Split
- globals['StaticLibrary'] = SCons.Defaults.StaticLibrary
- globals['StaticObject'] = SCons.Defaults.StaticObject
globals['TargetSignatures'] = TargetSignatures
globals['Tool'] = SCons.Tool.Tool
globals['Value'] = SCons.Node.Python.Value
spec.exists = sys.modules[full_name].exists
return spec
+def createProgBuilder(env):
+ """This is a utility function that creates the Program
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ program = env['BUILDERS']['Program']
+ except KeyError:
+ program = SCons.Builder.Builder(action=[ SCons.Defaults.StaticCheck,
+ '$LINKCOM' ],
+ emitter='$PROGEMITTER',
+ prefix='$PROGPREFIX',
+ suffix='$PROGSUFFIX',
+ src_suffix='$OBJSUFFIX',
+ src_builder='Object',
+ scanner = SCons.Defaults.ProgScan)
+ env['BUILDERS']['Program'] = program
+
+ return program
+
+def createStaticLibBuilder(env):
+ """This is a utility function that creates the StaticLibrary
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ static_lib = env['BUILDERS']['StaticLibrary']
+ except KeyError:
+ static_lib = SCons.Builder.Builder(action=[ SCons.Defaults.StaticCheck,
+ "$ARCOM" ],
+ emitter = '$LIBEMITTER',
+ prefix = '$LIBPREFIX',
+ suffix = '$LIBSUFFIX',
+ src_suffix = '$OBJSUFFIX',
+ src_builder = 'StaticObject')
+ env['BUILDERS']['StaticLibrary'] = static_lib
+ env['BUILDERS']['Library'] = static_lib
+
+ return static_lib
+
+def createSharedLibBuilder(env):
+ """This is a utility function that creates the SharedLibrary
+ Builder in an Environment if it is not there already.
+
+ If it is already there, we return the existing one.
+ """
+
+ try:
+ shared_lib = env['BUILDERS']['SharedLibrary']
+ except KeyError:
+ shared_lib = SCons.Builder.Builder(action=[ SCons.Defaults.SharedCheck,
+ "$SHLINKCOM" ],
+ emitter = "$SHLIBEMITTER",
+ prefix = '$SHLIBPREFIX',
+ suffix = '$SHLIBSUFFIX',
+ scanner = SCons.Defaults.ProgScan,
+ src_suffix = '$SHOBJSUFFIX',
+ src_builder = 'SharedObject')
+ env['BUILDERS']['SharedLibrary'] = shared_lib
+
+ return shared_lib
+
def createObjBuilders(env):
- """This is a utility function that creates the Object
+ """This is a utility function that creates the StaticObject
and SharedObject Builders in an Environment if they
are not there already.
"""
try:
- static_obj = env['BUILDERS']['Object']
+ static_obj = env['BUILDERS']['StaticObject']
except KeyError:
- static_obj = SCons.Defaults.StaticObject()
- env['BUILDERS']['Object'] = static_obj
+ static_obj = SCons.Builder.Builder(action = {},
+ emitter="$OBJEMITTER",
+ prefix = '$OBJPREFIX',
+ suffix = '$OBJSUFFIX',
+ src_builder = ['CFile', 'CXXFile'])
env['BUILDERS']['StaticObject'] = static_obj
+ env['BUILDERS']['Object'] = static_obj
try:
shared_obj = env['BUILDERS']['SharedObject']
except KeyError:
- shared_obj = SCons.Defaults.SharedObject()
+ shared_obj = SCons.Builder.Builder(action = {},
+ prefix = '$SHOBJPREFIX',
+ suffix = '$SHOBJSUFFIX',
+ emitter="$OBJEMITTER",
+ src_builder = ['CFile', 'CXXFile'])
env['BUILDERS']['SharedObject'] = shared_obj
return (static_obj, shared_obj)
try:
c_file = env['BUILDERS']['CFile']
except KeyError:
- c_file = SCons.Defaults.CFile()
+ c_file = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = '$CFILESUFFIX')
env['BUILDERS']['CFile'] = c_file
env['CFILESUFFIX'] = '.c'
try:
cxx_file = env['BUILDERS']['CXXFile']
except KeyError:
- cxx_file = SCons.Defaults.CXXFile()
+ cxx_file = SCons.Builder.Builder(action = {},
+ emitter = {},
+ suffix = '$CXXFILESUFFIX')
env['BUILDERS']['CXXFile'] = cxx_file
env['CXXFILESUFFIX'] = '.cc'
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
- bld = SCons.Defaults.StaticLibrary
- env['BUILDERS']['Library'] = bld
- env['BUILDERS']['StaticLibrary'] = bld
-
+ SCons.Tool.createStaticLibBuilder(env)
+
arcom = '$AR $ARFLAGS $TARGET $SOURCES'
ranlib = 'ranlib'
if env.Detect(ranlib):
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
def generate(env):
"""Add Builders and construction variables for ilink to an Environment."""
- env['BUILDERS']['Program'] = SCons.Defaults.Program
+ SCons.Tool.createProgBuilder(env)
env['LINK'] = 'ilink'
env['LINKFLAGS'] = ''
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
import SCons.Util
linkers = ['c++', 'cc']
def generate(env):
"""Add Builders and construction variables for gnulink to an Environment."""
- env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
- env['BUILDERS']['Program'] = SCons.Defaults.Program
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = '$LINKFLAGS -shared'
import SCons.Action
import SCons.Defaults
import SCons.Errors
+import SCons.Tool
import SCons.Util
from SCons.Tool.msvc import get_msdev_paths
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
- env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
- env['BUILDERS']['Program'] = SCons.Defaults.Program
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
env['SUBST_CMD_FILE'] = LinklocGenerator
env['SHLINK'] = '$LINK'
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
import SCons.Tool.msvs
import SCons.Tool.msvc
def generate(env):
"""Add Builders and construction variables for lib to an Environment."""
- env['BUILDERS']['Library'] = SCons.Defaults.StaticLibrary
- env['BUILDERS']['StaticLibrary'] = SCons.Defaults.StaticLibrary
+ SCons.Tool.createStaticLibBuilder(env)
try:
version = SCons.Tool.msvs.get_default_visualstudio_version(env)
import SCons.Defaults
import SCons.Errors
import SCons.Util
+import SCons.Tool
import SCons.Tool.msvs
import SCons.Tool.msvc
import SCons.Platform.win32
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
- env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
- env['BUILDERS']['Program'] = SCons.Defaults.Program
+ SCons.Tool.createSharedLibBuilder(env)
+ SCons.Tool.createProgBuilder(env)
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = '$LINKFLAGS /dll'
# We use the emitters of Program / StaticLibrary / SharedLibrary
# to produce almost all builders except .cpp from .ui
- try:
- static = env.StaticObject
- except AttributeError:
- static = SCons.Defaults.StaticObject
- try:
- shared = env.SharedObject
- except AttributeError:
- shared = SCons.Defaults.SharedObject
- env['PROGEMITTER'] = _Automoc(static,
+ # First, make sure the Environment has Object builders.
+ SCons.Tool.createObjBuilders(env)
+ # We can't refer to the builders directly, we have to fetch them
+ # as Environment attributes because that sets them up to be called
+ # correctly later by our emitter.
+ env['PROGEMITTER'] = _Automoc(env.StaticObject,
uicDeclBld,mocFromHBld,mocFromCppBld)
- env['SHLIBEMITTER'] = _Automoc(shared,
+ env['SHLIBEMITTER'] = _Automoc(env.SharedObject,
uicDeclBld,mocFromHBld,mocFromCppBld)
- env['LIBEMITTER'] = _Automoc(static,
+ env['LIBEMITTER'] = _Automoc(env.StaticObject,
uicDeclBld,mocFromHBld,mocFromCppBld)
# Of course, we need to link against the qt libraries
env.Append(CPPPATH=os.path.join('$QTDIR', 'include'))
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
- bld = SCons.Defaults.StaticLibrary
- env['BUILDERS']['Library'] = bld
- env['BUILDERS']['StaticLibrary'] = bld
+ SCons.Tool.createStaticLibBuilder(env)
if env.Detect('CC'):
env['AR'] = 'CC'
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
+import SCons.Tool
def generate(env):
"""Add Builders and construction variables for ar to an Environment."""
- bld = SCons.Defaults.StaticLibrary
- env['BUILDERS']['Library'] = bld
- env['BUILDERS']['StaticLibrary'] = bld
+ SCons.Tool.createStaticLibBuilder(env)
if env.Detect('CC'):
env['AR'] = 'CC'
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import os.path
+import string
+
import SCons.Tool
+import SCons.Util
+
+def _yaccEmitter(target, source, env, ysuf, hsuf):
+ # If -d is specified on the command line, yacc will emit a .h
+ # or .hpp file as well as a .c or .cpp file, depending on whether
+ # the input file is a .y or .yy, respectively.
+ if len(source) and '-d' in string.split(env.subst("$YACCFLAGS")):
+ base, ext = os.path.splitext(SCons.Util.to_String(source[0]))
+ if ext == ysuf:
+ target.append(base + hsuf)
+ return (target, source)
+
+def yEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, '.y', '.h')
+
+def yyEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, '.yy', '.hpp')
def generate(env):
"""Add Builders and construction variables for yacc to an Environment."""
c_file.add_action('.y', '$YACCCOM')
cxx_file.add_action('.yy', '$YACCCOM')
+ c_file.add_emitter('.y', yEmitter)
+ cxx_file.add_emitter('.yy', yyEmitter)
env['YACC'] = env.Detect('bison') or 'yacc'
env['YACCFLAGS'] = ''