"""SCons.Builder
-XXX
+Builder object subsystem.
+
+A Builder object is a callable that encapsulates information about how
+to execute actions to create a Node (file) from other Nodes (files), and
+how to create those dependencies for tracking.
+
+The main entry point here is the Builder() factory method. This
+provides a procedural interface that creates the right underlying
+Builder object based on the keyword arguments supplied and the types of
+the arguments.
+
+The goal is for this external interface to be simple enough that the
+vast majority of users can create new Builders as necessary to support
+building new types of files in their configurations, without having to
+dive any deeper into this subsystem.
"""
#
-# Copyright (c) 2001 Steven Knight
+# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
-import os
import os.path
+from SCons.Errors import InternalError, UserError
+
+import SCons.Action
+import SCons.Executor
+import SCons.Node
import SCons.Node.FS
-from SCons.Util import PathList, scons_str2nodes, scons_subst
-import string
-import types
-from UserList import UserList
-from UserDict import UserDict
-from Errors import UserError
+import SCons.Util
+import SCons.Warnings
-try:
- from UserString import UserString
-except ImportError:
- class UserString:
- pass
+class _Null:
+ pass
+_null = _Null
+class DictCmdGenerator:
+ """This is a callable class that can be used as a
+ command generator function. It holds on to a dictionary
+ mapping file suffixes to Actions. It uses that dictionary
+ to return the proper action based on the file suffix of
+ the source file."""
+
+ def __init__(self, action_dict):
+ self.action_dict = action_dict
-if os.name == 'posix':
+ def src_suffixes(self):
+ return self.action_dict.keys()
- def spawn(cmd, args, env):
- pid = os.fork()
- if not pid:
- # Child process.
- os.execvpe(cmd, args, env)
- else:
- # Parent process.
- pid, stat = os.waitpid(pid, 0)
- ret = stat >> 8
- return ret
-
-elif os.name == 'nt':
-
- def pathsearch(cmd, env):
- # In order to deal with the fact that 1.5.2 doesn't have
- # os.spawnvpe(), roll our own PATH search.
- if os.path.isabs(cmd):
- if not os.path.exists(cmd):
- exts = env['PATHEXT']
- if type(exts) != type([]):
- exts = string.split(exts, os.pathsep)
- for e in exts:
- f = cmd + e
- if os.path.exists(f):
- return f
- else:
- path = env['PATH']
- if type(path) != type([]):
- path = string.split(path, os.pathsep)
- exts = env['PATHEXT']
- if type(exts) != type([]):
- exts = string.split(exts, os.pathsep)
- pairs = []
- for dir in path:
- for e in [None] + exts:
- pairs.append(dir, e)
- for dir, ext in pairs:
- f = os.path.join(dir, cmd)
- if not ext is None:
- f = f + ext
- if os.path.exists(f):
- return f
- return cmd
-
- def spawn(cmd, args, env):
+ def add_action(self, suffix, action):
+ """Add a suffix-action pair to the mapping.
+ """
+ self.action_dict[suffix] = action
+
+ def __call__(self, target, source, env, for_signature):
+ ext = None
+ for src in map(str, source):
+ my_ext = SCons.Util.splitext(src)[1]
+ if ext and my_ext != ext:
+ raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
+ ext = my_ext
+
+ if not ext:
+ raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
try:
- ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
- except AttributeError:
- cmd = pathsearch(cmd, env)
- ret = os.spawnve(os.P_WAIT, cmd, args, env)
- return ret
-
+ return self.action_dict[ext]
+ except KeyError:
+ # Before raising the user error, try to perform Environment
+ # substitution on the keys of action_dict.
+ s_dict = {}
+ for (k,v) in self.action_dict.items():
+ s_k = env.subst(k)
+ if s_dict.has_key(s_k):
+ # XXX Note that we do only raise errors, when variables
+ # point to the same suffix. If one suffix is a
+ # literal and a variable suffix contains this literal
+ # we don't raise an error (cause the literal 'wins')
+ raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (s_dict[s_k][0], k, s_k))
+ s_dict[s_k] = (k,v)
+ try:
+ return s_dict[ext][1]
+ except KeyError:
+ raise UserError("While building `%s': Don't know how to build a file with suffix %s." % (repr(map(str, target)), repr(ext)))
+ def __cmp__(self, other):
+ return cmp(self.action_dict, other.action_dict)
def Builder(**kw):
"""A factory for builder objects."""
+ composite = None
+ if kw.has_key('generator'):
+ if kw.has_key('action'):
+ raise UserError, "You must not specify both an action and a generator."
+ kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
+ del kw['generator']
+ elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
+ composite = DictCmdGenerator(kw['action'])
+ 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('src_builder'):
- return apply(MultiStepBuilder, (), kw)
- elif kw.has_key('action') and (type(kw['action']) is types.DictType or
- isinstance(kw['action'], UserDict)):
- action_dict = kw['action']
- builders = []
- for suffix, action in action_dict.items():
- bld_kw = kw.copy()
- bld_kw['action'] = action
- bld_kw['src_suffix'] = suffix
- builders.append(apply(BuilderBase, (), bld_kw))
- del kw['action']
- kw['builders'] = builders
- return apply(CompositeBuilder, (), kw)
+ ret = apply(MultiStepBuilder, (), kw)
else:
- return apply(BuilderBase, (), kw)
+ ret = apply(BuilderBase, (), kw)
+
+ if composite:
+ ret = CompositeBuilder(ret, composite)
+ return ret
+
+def _init_nodes(builder, env, overrides, tlist, slist):
+ """Initialize lists of target and source nodes with all of
+ the proper Builder information.
+ """
+ # First, figure out if there are any errors in the way the targets
+ # were specified.
+ for t in tlist:
+ if t.side_effect:
+ raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
+ if t.has_builder():
+ if t.env != env:
+ raise UserError, "Two different environments were specified for the same target: %s"%str(t)
+ elif t.overrides != overrides:
+ raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
+ elif builder.scanner and builder.scanner != t.target_scanner:
+ raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
+
+ if builder.multi:
+ if t.builder != builder:
+ if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
+ raise UserError, "Two different target sets have a target in common: %s"%str(t)
+ else:
+ raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t))
+ elif t.sources != slist:
+ raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
+
+ # The targets are fine, so find or make the appropriate Executor to
+ # build this particular list of targets from this particular list of
+ # sources.
+ executor = None
+ if builder.multi:
+ try:
+ executor = tlist[0].get_executor(create = 0)
+ except AttributeError:
+ pass
+ else:
+ executor.add_sources(slist)
+ if executor is None:
+ executor = SCons.Executor.Executor(builder,
+ tlist[0].generate_build_env(env),
+ overrides,
+ tlist,
+ slist)
+
+ # Now set up the relevant information in the target Nodes themselves.
+ for t in tlist:
+ t.overrides = overrides
+ t.cwd = SCons.Node.FS.default_fs.getcwd()
+ t.builder_set(builder)
+ t.env_set(env)
+ t.add_source(slist)
+ t.set_executor(executor)
+ if builder.scanner:
+ t.target_scanner = builder.scanner
+
+ # Last, add scanners from the Environment to the source Nodes.
+ for s in slist:
+ src_key = s.scanner_key() # the file suffix
+ scanner = env.get_scanner(src_key)
+ if scanner:
+ s.source_scanner = scanner
+
+
+def _adjust_suffix(suff):
+ if suff and not suff[0] in [ '.', '$' ]:
+ return '.' + suff
+ return suff
+
+class EmitterProxy:
+ """This is a callable class that can act as a
+ Builder emitter. It holds on to a string that
+ is a key into an Environment dictionary, and will
+ look there at actual build time to see if it holds
+ a callable. If so, we will call that as the actual
+ emitter."""
+ def __init__(self, var):
+ self.var = SCons.Util.to_String(var)
+
+ def __call__(self, target, source, env):
+ emitter = self.var
+
+ # Recursively substitute the variable.
+ # We can't use env.subst() because it deals only
+ # in strings. Maybe we should change that?
+ while SCons.Util.is_String(emitter) and \
+ env.has_key(emitter):
+ emitter = env[emitter]
+ if not callable(emitter):
+ return (target, source)
+
+ return emitter(target, source, env)
+
+ def __cmp__(self, other):
+ return cmp(self.var, other.var)
class BuilderBase:
"""Base class for Builders, objects that create output
nodes (files) from input nodes (files).
"""
- def __init__(self, name = None,
- action = None,
- prefix = '',
- suffix = '',
- src_suffix = '',
- node_factory = SCons.Node.FS.default_fs.File):
- self.name = name
- self.action = Action(action)
-
- self.prefix = prefix
- self.suffix = suffix
- self.src_suffix = src_suffix
- self.node_factory = node_factory
- if self.suffix and self.suffix[0] not in '.$':
- self.suffix = '.' + self.suffix
- if self.src_suffix and self.src_suffix[0] not in '.$':
- self.src_suffix = '.' + self.src_suffix
+ def __init__(self, action = None,
+ prefix = '',
+ suffix = '',
+ src_suffix = '',
+ node_factory = SCons.Node.FS.default_fs.File,
+ target_factory = None,
+ source_factory = None,
+ scanner = None,
+ emitter = None,
+ multi = 0,
+ env = None,
+ overrides = {}):
+ self.action = SCons.Action.Action(action)
+ self.multi = multi
+ self.prefix = prefix
+ self.suffix = suffix
+ self.env = env
+ self.overrides = overrides
+
+ self.set_src_suffix(src_suffix)
+
+ self.target_factory = target_factory or node_factory
+ self.source_factory = source_factory or node_factory
+ self.scanner = scanner
+
+ self.emitter = emitter
+
+ def __nonzero__(self):
+ raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
+
+ def get_name(self, env):
+ """Attempts to get the name of the Builder.
+
+ Look at the BUILDERS variable of env, expecting it to be a
+ dictionary containing this Builder, and return the key of the
+ dictionary."""
+
+ try:
+ index = env['BUILDERS'].values().index(self)
+ return env['BUILDERS'].keys()[index]
+ except (AttributeError, KeyError, ValueError):
+ return str(self.__class__)
def __cmp__(self, other):
- return cmp(self.__dict__, other.__dict__)
-
- def __call__(self, env, target = None, source = None):
- def adjustixes(files, pre, suf):
- ret = []
- if not type(files) is type([]):
- files = [files]
- for f in files:
- if type(f) == type(""):
- if pre and f[:len(pre)] != pre:
- f = pre + f
- if suf:
- if f[-len(suf):] != suf:
- f = f + suf
- ret.append(f)
- return ret
-
- tlist = scons_str2nodes(adjustixes(target,
- env.subst(self.prefix),
- env.subst(self.suffix)),
- self.node_factory)
-
- slist = scons_str2nodes(adjustixes(source, None,
- env.subst(self.src_suffix)),
- self.node_factory)
-
- for t in tlist:
- t.builder_set(self)
- t.env_set(env)
- t.add_source(slist)
-
- for s in slist:
- s.env_set(env, 1)
-
- if len(tlist) == 1:
- tlist = tlist[0]
- return tlist
-
- def execute(self, **kw):
- """Execute a builder's action to create an output object.
- """
- return apply(self.action.execute, (), kw)
-
- def get_contents(self, **kw):
- """Fetch the "contents" of the builder's action
- (for signature calculation).
+ return cmp(self.__dict__, other.__dict__)
+
+ def _create_nodes(self, env, overrides, target = None, source = None):
+ """Create and return lists of target and source nodes.
+ """
+ def adjustixes(files, pre, suf):
+ if not files:
+ return []
+ ret = []
+ if not SCons.Util.is_List(files):
+ files = [files]
+
+ for f in files:
+ if SCons.Util.is_String(f):
+ if pre:
+ path, fn = os.path.split(os.path.normpath(f))
+ if fn[:len(pre)] != pre:
+ f = os.path.join(path, pre + fn)
+ # Only append a suffix if the file does not have one.
+ if suf and not SCons.Util.splitext(f)[1]:
+ if f[-len(suf):] != suf:
+ f = f + suf
+ ret.append(f)
+ return ret
+
+ env = env.Override(overrides)
+
+ pre = self.get_prefix(env)
+ suf = self.get_suffix(env)
+ src_suf = self.get_src_suffix(env)
+
+ source = adjustixes(source, None, src_suf)
+ if target is None:
+ s = source[0]
+ if isinstance(s, SCons.Node.Node):
+ s = str(s)
+ dir, s = os.path.split(s)
+ target = pre + os.path.splitext(s)[0] + suf
+ if dir:
+ target = [ os.path.join(dir, target) ]
+ else:
+ target = adjustixes(target, pre, suf)
+
+ slist = SCons.Node.arg2nodes(source, self.source_factory)
+ tlist = SCons.Node.arg2nodes(target, self.target_factory)
+
+ if self.emitter:
+ # The emitter is going to do str(node), but because we're
+ # being called *from* a builder invocation, the new targets
+ # don't yet have a builder set on them and will look like
+ # source files. Fool the emitter's str() calls by setting
+ # up a temporary builder on the new targets.
+ new_targets = []
+ for t in tlist:
+ if not t.is_derived():
+ t.builder = self
+ new_targets.append(t)
+
+ target, source = self.emitter(target=tlist, source=slist, env=env)
+
+ # Now delete the temporary builders that we attached to any
+ # new targets, so that _init_nodes() doesn't do weird stuff
+ # to them because it thinks they already have builders.
+ for t in new_targets:
+ if t.builder is self:
+ # Only delete the temporary builder if the emitter
+ # didn't change it on us.
+ t.builder = None
+
+ # Have to call arg2nodes yet again, since it is legal for
+ # emitters to spit out strings as well as Node instances.
+ slist = SCons.Node.arg2nodes(source, self.source_factory)
+ tlist = SCons.Node.arg2nodes(target, self.target_factory)
+
+ return tlist, slist
+
+ def __call__(self, env, target = None, source = _null, **overrides):
+ if source is _null:
+ source = target
+ target = None
+ tlist, slist = self._create_nodes(env, overrides, target, source)
+
+ if len(tlist) == 1:
+ _init_nodes(self, env, overrides, tlist, slist)
+ tlist = tlist[0]
+ else:
+ _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist)
+
+ return tlist
+
+ def src_suffixes(self, env):
+ return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
+ self.src_suffix)
+
+ def set_src_suffix(self, src_suffix):
+ if not src_suffix:
+ src_suffix = []
+ elif not SCons.Util.is_List(src_suffix):
+ src_suffix = [ src_suffix ]
+ self.src_suffix = src_suffix
+
+ def get_src_suffix(self, env):
+ """Get the first src_suffix in the list of src_suffixes."""
+ ret = self.src_suffixes(env)
+ if not ret:
+ return ''
+ return ret[0]
+
+ def get_suffix(self, env):
+ return env.subst(_adjust_suffix(self.suffix))
+
+ def get_prefix(self, env):
+ return env.subst(self.prefix)
+
+ def targets(self, node):
+ """Return the list of targets for this builder instance.
+
+ For most normal builders, this is just the supplied node.
"""
- return apply(self.action.get_contents, (), kw)
+ return [ node ]
+
+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.
+ """
+
+ def __init__(self, builder, env, tlist):
+ SCons.Util.Proxy.__init__(self, builder)
+ self.builder = builder
+ self.scanner = builder.scanner
+ self.env = env
+ self.tlist = tlist
+ self.multi = builder.multi
+
+ def targets(self, node):
+ """Return the list of targets for this builder instance.
+ """
+ return self.tlist
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
+ def get_name(self, env):
+ """Attempts to get the name of the Builder."""
+
+ return "ListBuilder(%s)" % self.builder.get_name(env)
class MultiStepBuilder(BuilderBase):
"""This is a builder subclass that can build targets in
src_suffix.
"""
def __init__(self, src_builder,
- name = None,
- action = None,
- prefix = '',
- suffix = '',
- src_suffix = '',
- node_factory = SCons.Node.FS.default_fs.File):
- BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
- node_factory)
+ action = None,
+ prefix = '',
+ suffix = '',
+ src_suffix = '',
+ node_factory = SCons.Node.FS.default_fs.File,
+ target_factory = None,
+ source_factory = None,
+ scanner=None,
+ emitter=None):
+ BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
+ node_factory, target_factory, source_factory,
+ scanner, emitter)
+ if not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
self.src_builder = src_builder
+ self.sdict = {}
+ self.cached_src_suffixes = {} # source suffixes keyed on id(env)
+
+ def __call__(self, env, target = None, source = _null, **kw):
+ if source is _null:
+ source = target
+ target = None
- def __call__(self, env, target = None, source = None):
- slist = scons_str2nodes(source, self.node_factory)
+ slist = SCons.Node.arg2nodes(source, self.source_factory)
final_sources = []
- src_suffix = env.subst(self.src_suffix)
+
+ try:
+ sdict = self.sdict[id(env)]
+ except KeyError:
+ sdict = {}
+ self.sdict[id(env)] = sdict
+ for bld in self.src_builder:
+ if SCons.Util.is_String(bld):
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ for suf in bld.src_suffixes(env):
+ sdict[suf] = bld
+
+ src_suffixes = self.src_suffixes(env)
+
for snode in slist:
- path, ext = os.path.splitext(snode.path)
- if not src_suffix or ext != src_suffix:
- tgt = self.src_builder(env, target = [ path ],
- source=snode)
- if not type(tgt) is types.ListType:
+ path, ext = SCons.Util.splitext(snode.get_abspath())
+ if sdict.has_key(ext):
+ src_bld = sdict[ext]
+ tgt = apply(src_bld, (env, path, snode), kw)
+ # Only supply the builder with sources it is capable
+ # of building.
+ if SCons.Util.is_List(tgt):
+ tgt = filter(lambda x, suf=src_suffixes:
+ SCons.Util.splitext(SCons.Util.to_String(x))[1] in suf,
+ tgt)
+ if not SCons.Util.is_List(tgt):
final_sources.append(tgt)
else:
final_sources.extend(tgt)
else:
final_sources.append(snode)
- return BuilderBase.__call__(self, env, target=target,
- source=final_sources)
-
-class CompositeBuilder(BuilderBase):
- """This is a convenient Builder subclass that can build different
- files based on their suffixes. For each target, this builder
- will examine the target's sources. If they are all the same
- suffix, and that suffix is equal to one of the child builders'
- src_suffix, then that child builder will be used. Otherwise,
- UserError is thrown.
-
- Child builders are supplied via the builders arg to the
- constructor. Each must have its src_suffix set."""
- def __init__(self, name = None,
- prefix='',
- suffix='',
- builders=[]):
- BuilderBase.__init__(self, name=name, prefix=prefix,
- suffix=suffix)
- self.builder_dict = {}
- for bld in builders:
- if not bld.src_suffix:
- raise InternalError, "All builders supplied to CompositeBuilder class must have a src_suffix."
- self.builder_dict[bld.src_suffix] = bld
-
- def __call__(self, env, target = None, source = None):
- ret = BuilderBase.__call__(self, env, target=target, source=source)
-
- builder_dict = {}
- for suffix, bld in self.builder_dict.items():
- builder_dict[env.subst(bld.src_suffix)] = bld
-
- if type(ret) is types.ListType:
- tlist = ret
- else:
- tlist = [ ret ]
- for tnode in tlist:
- suflist = map(lambda x: os.path.splitext(x.path)[1],
- tnode.sources)
- last_suffix=''
- for suffix in suflist:
- if last_suffix and last_suffix != suffix:
- raise UserError, "The builder for %s is only capable of building source files of identical suffixes." % tnode.path
- last_suffix = suffix
- if last_suffix:
- try:
- tnode.builder_set(builder_dict[last_suffix])
- except KeyError:
- raise UserError, "Builder not capable of building files with suffix: %s" % suffix
- return ret
-print_actions = 1;
-execute_actions = 1;
-
-def Action(act):
- """A factory for action objects."""
- if callable(act):
- return FunctionAction(act)
- elif type(act) == types.StringType or isinstance(act, UserString):
- return CommandAction(act)
- elif type(act) == types.ListType or isinstance(act, UserList):
- return ListAction(act)
- else:
- return None
-
-class ActionBase:
- """Base class for actions that create output objects.
-
- We currently expect Actions will only be accessible through
- Builder objects, so they don't yet merit their own module."""
- def __cmp__(self, other):
- return cmp(self.__dict__, other.__dict__)
+ return apply(BuilderBase.__call__,
+ (self, env, target, final_sources), kw)
- def show(self, string):
- print string
+ def get_src_builders(self, env):
+ """Return all the src_builders for this Builder.
- def subst_dict(self, **kw):
- """Create a dictionary for substitution of construction
- variables.
-
- This translates the following special arguments:
-
- env - the construction environment itself,
- the values of which (CC, CCFLAGS, etc.)
- are copied straight into the dictionary
-
- target - the target (object or array of objects),
- used to generate the TARGET and TARGETS
- construction variables
-
- source - the source (object or array of objects),
- used to generate the SOURCES construction
- variable
-
- Any other keyword arguments are copied into the
- dictionary."""
-
- dict = {}
- if kw.has_key('env'):
- dict.update(kw['env'])
- del kw['env']
-
- if kw.has_key('target'):
- t = kw['target']
- del kw['target']
- if type(t) is type(""):
- t = [t]
- dict['TARGETS'] = PathList(map(os.path.normpath, t))
- dict['TARGET'] = dict['TARGETS'][0]
- if kw.has_key('source'):
- s = kw['source']
- del kw['source']
- if type(s) is type(""):
- s = [s]
- dict['SOURCES'] = PathList(map(os.path.normpath, s))
-
- dict.update(kw)
-
- return dict
-
-class CommandAction(ActionBase):
- """Class for command-execution actions."""
- def __init__(self, string):
- self.command = string
-
- def execute(self, **kw):
- dict = apply(self.subst_dict, (), kw)
- cmd_str = scons_subst(self.command, dict, {})
- for cmd in string.split(cmd_str, '\n'):
- if print_actions:
- self.show(cmd)
- if execute_actions:
- args = string.split(cmd)
- try:
- ENV = kw['env']['ENV']
- except:
- import SCons.Defaults
- ENV = SCons.Defaults.ConstructionEnvironment['ENV']
- ret = spawn(args[0], args, ENV)
- if ret:
- return ret
- return 0
-
- def get_contents(self, **kw):
- """Return the signature contents of this action's command line.
-
- For signature purposes, it doesn't matter what targets or
- sources we use, so long as we use the same ones every time
- so the signature stays the same. We supply an array of two
- of each to allow for distinction between TARGET and TARGETS.
+ This is essentially a recursive descent of the src_builder "tree."
"""
- kw['target'] = ['__t1__', '__t2__']
- kw['source'] = ['__s1__', '__s2__']
- dict = apply(self.subst_dict, (), kw)
- return scons_subst(self.command, dict, {})
-
-class FunctionAction(ActionBase):
- """Class for Python function actions."""
- def __init__(self, function):
- self.function = function
-
- def execute(self, **kw):
- # if print_actions:
- # XXX: WHAT SHOULD WE PRINT HERE?
- if execute_actions:
- dict = apply(self.subst_dict, (), kw)
- return apply(self.function, (), dict)
-
- def get_contents(self, **kw):
- """Return the signature contents of this callable action.
-
- By providing direct access to the code object of the
- function, Python makes this extremely easy. Hooray!
+ ret = []
+ for bld in self.src_builder:
+ if SCons.Util.is_String(bld):
+ # All Environments should have a BUILDERS
+ # variable, so no need to check for it.
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ ret.append(bld)
+ return ret
+
+ def src_suffixes(self, env):
+ """Return a list of the src_suffix attributes for all
+ src_builders of this Builder.
"""
- #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES
- #THE FUNCTION MAY USE
try:
- # "self.function" is a function.
- code = self.function.func_code.co_code
- except:
- # "self.function" is a callable object.
- code = self.function.__call__.im_func.func_code.co_code
- return str(code)
-
-class ListAction(ActionBase):
- """Class for lists of other actions."""
- def __init__(self, list):
- self.list = map(lambda x: Action(x), list)
-
- def execute(self, **kw):
- for l in self.list:
- r = apply(l.execute, (), kw)
- if r != 0:
- return r
- return 0
-
- def get_contents(self, **kw):
- """Return the signature contents of this action list.
-
- Simple concatenation of the signatures of the elements.
- """
+ return self.cached_src_suffixes[id(env)]
+ except KeyError:
+ suffixes = BuilderBase.src_suffixes(self, env)
+ for builder in self.get_src_builders(env):
+ suffixes.extend(builder.src_suffixes(env))
+ self.cached_src_suffixes[id(env)] = suffixes
+ return suffixes
+
+class CompositeBuilder(SCons.Util.Proxy):
+ """A Builder Proxy whose main purpose is to always have
+ a DictCmdGenerator as its action, and to provide access
+ to the DictCmdGenerator's add_action() method.
+ """
+
+ def __init__(self, builder, cmdgen):
+ SCons.Util.Proxy.__init__(self, builder)
- return reduce(lambda x, y: x + str(y.get_contents()), self.list, "")
+ # cmdgen should always be an instance of DictCmdGenerator.
+ self.cmdgen = cmdgen
+ self.builder = builder
+
+ def add_action(self, suffix, action):
+ self.cmdgen.add_action(suffix, action)
+ self.set_src_suffix(self.cmdgen.src_suffixes())
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)