"""
#
-# Copyright (c) 2001, 2002 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.path
-import string
-import copy
-from SCons.Errors import UserError
+from SCons.Errors import InternalError, UserError
import SCons.Action
+import SCons.Executor
import SCons.Node
import SCons.Node.FS
import SCons.Util
import SCons.Warnings
+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
"""
self.action_dict[suffix] = action
- def __call__(self, source, target, env, **kw):
+ def __call__(self, target, source, env, for_signature):
ext = None
for src in map(str, source):
- my_ext = os.path.splitext(src)[1]
+ my_ext = SCons.Util.splitext(src)[1]
if ext and my_ext != ext:
- raise UserError("Cannot build multiple sources with different extensions.")
+ 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 ext is None:
- raise UserError("Cannot deduce file extension from source files: %s" % repr(map(str, source)))
+ 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:
- # XXX Do we need to perform Environment substitution
- # on the keys of action_dict before looking it up?
return self.action_dict[ext]
except KeyError:
- raise UserError("Don't know how to build a file with suffix %s." % ext)
+ # 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('name'):
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "The use of the 'name' parameter to Builder() is deprecated.")
if kw.has_key('generator'):
if kw.has_key('action'):
raise UserError, "You must not specify both an action and a generator."
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'):
ret = apply(MultiStepBuilder, (), kw)
else:
return ret
-def _init_nodes(builder, env, args, tlist, slist):
+def _init_nodes(builder, env, overrides, tlist, slist):
"""Initialize lists of target and source nodes with all of
the proper Builder information.
"""
- for s in slist:
- src_key = s.scanner_key() # the file suffix
- scanner = env.get_scanner(src_key)
- if scanner:
- s.source_scanner = scanner
-
+ # First, figure out if there are any errors in the way the targets
+ # were specified.
for t in tlist:
- if t.builder is not None:
- if t.env != env:
+ 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.build_args != args:
- raise UserError, "Two different sets of build arguments 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)
elif t.sources != slist:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
- t.build_args = args
+ # 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
def __init__(self, var):
self.var = SCons.Util.to_String(var)
- def __call__(self, target, source, env, **kw):
+ def __call__(self, target, source, env):
emitter = self.var
# Recursively substitute the variable.
emitter = env[emitter]
if not callable(emitter):
return (target, source)
- args = { 'target':target,
- 'source':source,
- 'env':env }
- args.update(kw)
- return apply(emitter, (), args)
+
+ return emitter(target, source, env)
def __cmp__(self, other):
return cmp(self.var, other.var)
nodes (files) from input nodes (files).
"""
- def __init__(self, name = None,
- action = None,
+ def __init__(self, action = None,
prefix = '',
suffix = '',
src_suffix = '',
source_factory = None,
scanner = None,
emitter = None,
- multi = 0):
- self.name = name
+ 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.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.
- 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
+ Look at the BUILDERS variable of env, expecting it to be a
+ dictionary containing this Builder, and return the key of the
dictionary."""
- if self.name:
- return self.name
try:
index = env['BUILDERS'].values().index(self)
return env['BUILDERS'].keys()[index]
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
- def _create_nodes(self, env, args, target = None, source = None):
+ def _create_nodes(self, env, overrides, target = None, source = None):
"""Create and return lists of target and source nodes.
"""
def adjustixes(files, pre, suf):
for f in files:
if SCons.Util.is_String(f):
- if pre and f[:len(pre)] != pre:
+ if pre:
path, fn = os.path.split(os.path.normpath(f))
- f = os.path.join(path, pre + fn)
+ 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 os.path.splitext(f)[1]:
- if f[-len(suf):] != suf:
- f = f + suf
- ret.append(f)
- return ret
+ 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:
- # pass the targets and sources to the emitter as strings
- # rather than nodes since str(node) doesn't work
- # properly from any directory other than the top directory,
- # and emitters are called "in" the SConscript directory:
- tlist = adjustixes(target, pre, suf)
- slist = adjustixes(source, None, src_suf)
-
- emit_args = { 'target' : tlist,
- 'source' : slist,
- 'env' : env }
- emit_args.update(args)
- target, source = apply(self.emitter, (), emit_args)
-
- tlist = SCons.Node.arg2nodes(adjustixes(target, pre, suf),
- self.target_factory)
- slist = SCons.Node.arg2nodes(adjustixes(source, None, src_suf),
- self.source_factory)
+ # 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 = None, **kw):
- tlist, slist = self._create_nodes(env, kw, target, source)
+ 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, kw, tlist, slist)
+ _init_nodes(self, env, overrides, tlist, slist)
tlist = tlist[0]
else:
- _init_nodes(ListBuilder(self, env, tlist), env, kw, tlist, slist)
+ _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist)
return tlist
- def execute(self, **kw):
- """Execute a builder's action to create an output object.
- """
- return apply(self.action.execute, (), kw)
-
- def get_raw_contents(self, **kw):
- """Fetch the "contents" of the builder's action.
- """
- return apply(self.action.get_raw_contents, (), kw)
-
- def get_contents(self, **kw):
- """Fetch the "contents" of the builder's action
- (for signature calculation).
- """
- return apply(self.action.get_contents, (), kw)
-
def src_suffixes(self, env):
return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
self.src_suffix)
self.env = env
self.tlist = tlist
self.multi = builder.multi
- self.name = "ListBuilder(%s)"%builder.name
-
- def execute(self, **kw):
- if hasattr(self, 'status'):
- return self.status
- for t in self.tlist:
- # unlink all targets and make all directories
- # before building anything
- t.prepare()
- kw['target'] = self.tlist
- self.status = apply(self.builder.execute, (), kw)
- for t in self.tlist:
- if not t is kw['target']:
- t.build()
- return self.status
def targets(self, node):
"""Return the list of targets for this builder instance.
return cmp(self.__dict__, other.__dict__)
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."""
+ """Attempts to get the name of the Builder."""
return "ListBuilder(%s)" % self.builder.get_name(env)
src_suffix.
"""
def __init__(self, src_builder,
- name = None,
action = None,
prefix = '',
suffix = '',
source_factory = None,
scanner=None,
emitter=None):
- BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
+ 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, **kw):
slist = SCons.Node.arg2nodes(source, self.source_factory)
final_sources = []
- r=repr(env)
try:
- sdict = self.sdict[r]
+ sdict = self.sdict[id(env)]
except KeyError:
sdict = {}
- self.sdict[r] = sdict
+ self.sdict[id(env)] = sdict
for bld in self.src_builder:
if SCons.Util.is_String(bld):
try:
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.abspath)
+ path, ext = SCons.Util.splitext(snode.get_abspath())
if sdict.has_key(ext):
src_bld = sdict[ext]
-
- dictArgs = copy.copy(kw)
- dictArgs['target'] = [path]
- dictArgs['source'] = snode
- dictArgs['env'] = env
- tgt = apply(src_bld, (), dictArgs)
- if not SCons.Util.is_List(tgt):
- tgt = [ tgt ]
-
+ tgt = apply(src_bld, (env, path, snode), kw)
# Only supply the builder with sources it is capable
# of building.
- tgt = filter(lambda x,
- suf=self.src_suffixes(env):
- os.path.splitext(SCons.Util.to_String(x))[1] in \
- suf, tgt)
- final_sources.extend(tgt)
+ 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)
- dictKwArgs = kw
- dictKwArgs['target'] = target
- dictKwArgs['source'] = final_sources
+
return apply(BuilderBase.__call__,
- (self, env), dictKwArgs)
+ (self, env, target, final_sources), kw)
def get_src_builders(self, env):
"""Return all the src_builders for this Builder.
"""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
+ try:
+ 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