summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Builder.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Builder.py')
-rw-r--r--src/engine/SCons/Builder.py187
1 files changed, 126 insertions, 61 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 94a8946..4dd6163 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -1,6 +1,20 @@
"""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.
"""
@@ -55,6 +69,11 @@ class DictCmdGenerator:
def src_suffixes(self):
return self.action_dict.keys()
+ def add_action(self, suffix, action):
+ """Add a suffix-action pair to the mapping.
+ """
+ self.action_dict[suffix] = action
+
def __call__(self, source, target, env, **kw):
ext = None
for src in map(str, source):
@@ -71,9 +90,12 @@ class DictCmdGenerator:
return self.action_dict[ext]
except KeyError:
raise UserError("Don't know how to build a file with suffix %s." % 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('name'):
SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
"The use of the 'name' parameter to Builder() is deprecated.")
@@ -83,9 +105,9 @@ def Builder(**kw):
kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
del kw['generator']
elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
- action_dict = kw['action']
- kw['action'] = SCons.Action.CommandGenerator(DictCmdGenerator(action_dict))
- kw['src_suffix'] = action_dict.keys()
+ 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']):
@@ -99,9 +121,14 @@ def Builder(**kw):
kw['emitter'] = EmitterProxy(var)
if kw.has_key('src_builder'):
- return apply(MultiStepBuilder, (), 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, args, tlist, slist):
"""Initialize lists of target and source nodes with all of
@@ -139,25 +166,7 @@ def _init_nodes(builder, env, args, tlist, slist):
t.add_source(slist)
if builder.scanner:
t.target_scanner = builder.scanner
-
-class _callable_adaptor:
- """When crteating a Builder, you can pass a string OR
- a callable in for prefix, suffix, or src_suffix.
- src_suffix even takes a list!
-
- If a string or list is passed, we use this class to
- adapt it to a callable."""
- def __init__(self, static):
- self.static = static
-
- def __call__(self, **kw):
- return self.static
-
- def __cmp__(self, other):
- if isinstance(other, _callable_adaptor):
- return cmp(self.static, other.static)
- return -1
-
+
def _adjust_suffix(suff):
if suff and not suff[0] in [ '.', '$' ]:
return '.' + suff
@@ -176,7 +185,7 @@ class EmitterProxy:
def __call__(self, target, source, env, **kw):
emitter = self.var
- # Recursively substitue the variable.
+ # 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 \
@@ -190,6 +199,9 @@ class EmitterProxy:
args.update(kw)
return apply(emitter, (), args)
+ 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).
@@ -212,10 +224,7 @@ class BuilderBase:
self.prefix = prefix
self.suffix = suffix
- if SCons.Util.is_String(src_suffix):
- self.src_suffix = [ src_suffix ]
- else:
- self.src_suffix = src_suffix
+ self.set_src_suffix(src_suffix)
self.target_factory = target_factory or node_factory
self.source_factory = source_factory or node_factory
@@ -264,9 +273,9 @@ class BuilderBase:
ret.append(f)
return ret
- pre = self.get_prefix(env, args)
- suf = self.get_suffix(env, args)
- src_suf = self.get_src_suffix(env, args)
+ pre = self.get_prefix(env)
+ suf = self.get_suffix(env)
+ src_suf = self.get_src_suffix(env)
if self.emitter:
# pass the targets and sources to the emitter as strings
# rather than nodes since str(node) doesn't work
@@ -299,7 +308,6 @@ class BuilderBase:
return tlist
-
def execute(self, **kw):
"""Execute a builder's action to create an output object.
"""
@@ -316,21 +324,28 @@ class BuilderBase:
"""
return apply(self.action.get_contents, (), kw)
- def src_suffixes(self, env, args):
+ def src_suffixes(self, env):
return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
self.src_suffix)
- def get_src_suffix(self, env, args):
+ 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."""
- if not self.src_suffix:
+ ret = self.src_suffixes(env)
+ if not ret:
return ''
- else:
- return self.src_suffix[0]
+ return ret[0]
- def get_suffix(self, env, args):
+ def get_suffix(self, env):
return env.subst(_adjust_suffix(self.suffix))
- def get_prefix(self, env, args):
+ def get_prefix(self, env):
return env.subst(self.prefix)
def targets(self, node):
@@ -340,20 +355,19 @@ class BuilderBase:
"""
return [ node ]
-class ListBuilder:
- """This is technically not a Builder object, but a wrapper
- around another Builder object. This is designed to look
- like a Builder object, though, for purposes of building an
- array of targets from a single Action execution.
+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.name = "ListBuilder(%s)"%builder.get_name(env)
-
- def get_name(self, env):
- return self.name
+ self.multi = builder.multi
+ self.name = "ListBuilder(%s)"%builder.name
def execute(self, **kw):
if hasattr(self, 'status'):
@@ -375,10 +389,17 @@ class ListBuilder:
return self.tlist
def __cmp__(self, other):
- return cmp(self.__dict__, other.__dict__)
+ return cmp(self.__dict__, other.__dict__)
- def __getattr__(self, name):
- return getattr(self.builder, name)
+ def get_name(self, env):
+ """Attempts to get the name of the Builder.
+
+ If the Builder's name attribute is None, then we will look at
+ the BUILDERS variable of env, expecting it to be a dictionary
+ containing this Builder, and we will return the key of the
+ dictionary."""
+
+ return "ListBuilder(%s)" % self.builder.get_name(env)
class MultiStepBuilder(BuilderBase):
"""This is a builder subclass that can build targets in
@@ -421,7 +442,12 @@ class MultiStepBuilder(BuilderBase):
sdict = {}
self.sdict[r] = sdict
for bld in self.src_builder:
- for suf in bld.src_suffixes(env, kw):
+ if SCons.Util.is_String(bld):
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ for suf in bld.src_suffixes(env):
sdict[suf] = bld
for snode in slist:
@@ -440,7 +466,7 @@ class MultiStepBuilder(BuilderBase):
# Only supply the builder with sources it is capable
# of building.
tgt = filter(lambda x,
- suf=self.src_suffixes(env, kw):
+ suf=self.src_suffixes(env):
os.path.splitext(SCons.Util.to_String(x))[1] in \
suf, tgt)
final_sources.extend(tgt)
@@ -452,9 +478,48 @@ class MultiStepBuilder(BuilderBase):
return apply(BuilderBase.__call__,
(self, env), dictKwArgs)
- def src_suffixes(self, env, args):
- return BuilderBase.src_suffixes(self, env, args) + \
- reduce(lambda x, y: x + y,
- map(lambda b, e=env, args=args: b.src_suffixes(e, args),
- self.src_builder),
- [])
+ def get_src_builders(self, env):
+ """Return all the src_builders for this Builder.
+
+ This is essentially a recursive descent of the src_builder "tree."
+ """
+ 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.
+ """
+ suffixes = BuilderBase.src_suffixes(self, env)
+ for builder in self.get_src_builders(env):
+ suffixes.extend(builder.src_suffixes(env))
+ 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)
+
+ # 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__)