3
Builder object subsystem.
5
A Builder object is a callable that encapsulates information about how
6
to execute actions to create a target Node (file) from source Nodes
7
(files), and how to create those dependencies for tracking.
9
The main entry point here is the Builder() factory method. This provides
10
a procedural interface that creates the right underlying Builder object
11
based on the keyword arguments supplied and the types of the arguments.
13
The goal is for this external interface to be simple enough that the
14
vast majority of users can create new Builders as necessary to support
15
building new types of files in their configurations, without having to
16
dive any deeper into this subsystem.
18
The base class here is BuilderBase. This is a concrete base class which
19
does, in fact, represent the Builder objects that we (or users) create.
21
There is also a proxy that looks like a Builder:
25
This proxies for a Builder with an action that is actually a
26
dictionary that knows how to map file suffixes to a specific
27
action. This is so that we can invoke different actions
28
(compilers, compile options) for different flavors of source
31
Builders and their proxies have the following public interface methods
32
used by other modules:
35
THE public interface. Calling a Builder object (with the
36
use of internal helper methods) sets up the target and source
37
dependencies, appropriate mapping to a specific action, and the
38
environment manipulation necessary for overridden construction
39
variable. This also takes care of warning about possible mistakes
43
Adds an emitter for a specific file suffix, used by some Tool
44
modules to specify that (for example) a yacc invocation on a .y
45
can create a .h *and* a .c file.
48
Adds an action for a specific file suffix, heavily used by
49
Tool modules to add their specific action(s) for turning
50
a source file into an object file to the global static
51
and shared object file Builders.
53
There are the following methods for internal use within this module:
56
The internal method that handles the heavily lifting when a
57
Builder is called. This is used so that the __call__() methods
58
can set up warning about possible mistakes in keyword-argument
59
overrides, and *then* execute all of the steps necessary so that
60
the warnings only occur once.
63
Returns the Builder's name within a specific Environment,
64
primarily used to try to return helpful information in error
72
Miscellaneous stuff for handling the prefix and suffix
73
manipulation we use in turning source file names into target
79
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
81
# Permission is hereby granted, free of charge, to any person obtaining
82
# a copy of this software and associated documentation files (the
83
# "Software"), to deal in the Software without restriction, including
84
# without limitation the rights to use, copy, modify, merge, publish,
85
# distribute, sublicense, and/or sell copies of the Software, and to
86
# permit persons to whom the Software is furnished to do so, subject to
87
# the following conditions:
89
# The above copyright notice and this permission notice shall be included
90
# in all copies or substantial portions of the Software.
92
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
93
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
94
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
95
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
96
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
97
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
98
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
101
__revision__ = "src/engine/SCons/Builder.py 3603 2008/10/10 05:46:45 scons"
107
from SCons.Debug import logInstanceCreation
108
from SCons.Errors import InternalError, UserError
109
import SCons.Executor
114
import SCons.Warnings
121
class DictCmdGenerator(SCons.Util.Selector):
122
"""This is a callable class that can be used as a
123
command generator function. It holds on to a dictionary
124
mapping file suffixes to Actions. It uses that dictionary
125
to return the proper action based on the file suffix of
128
def __init__(self, dict=None, source_ext_match=1):
129
SCons.Util.Selector.__init__(self, dict)
130
self.source_ext_match = source_ext_match
132
def src_suffixes(self):
135
def add_action(self, suffix, action):
136
"""Add a suffix-action pair to the mapping.
138
self[suffix] = action
140
def __call__(self, target, source, env, for_signature):
144
if self.source_ext_match:
146
for src in map(str, source):
147
my_ext = SCons.Util.splitext(src)[1]
148
if ext and my_ext != ext:
149
raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
152
ext = SCons.Util.splitext(str(source[0]))[1]
155
raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
158
ret = SCons.Util.Selector.__call__(self, env, source)
160
raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
162
raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \
163
(repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys())))
166
class CallableSelector(SCons.Util.Selector):
167
"""A callable dictionary that will, in turn, call the value it
169
def __call__(self, env, source):
170
value = SCons.Util.Selector.__call__(self, env, source)
172
value = value(env, source)
175
class DictEmitter(SCons.Util.Selector):
176
"""A callable dictionary that maps file suffixes to emitters.
177
When called, it finds the right emitter in its dictionary for the
178
suffix of the first source file, and calls that emitter to get the
179
right lists of targets and sources to return. If there's no emitter
180
for the suffix in its dictionary, the original target and source are
183
def __call__(self, target, source, env):
184
emitter = SCons.Util.Selector.__call__(self, env, source)
186
target, source = emitter(target, source, env)
187
return (target, source)
189
class ListEmitter(UserList.UserList):
190
"""A callable list of emitters that calls each in sequence,
191
returning the result.
193
def __call__(self, target, source, env):
195
target, source = e(target, source, env)
196
return (target, source)
198
# These are a common errors when calling a Builder;
199
# they are similar to the 'target' and 'source' keyword args to builders,
200
# so we issue warnings when we see them. The warnings can, of course,
202
misleading_keywords = {
203
'targets' : 'target',
204
'sources' : 'source',
207
class OverrideWarner(UserDict.UserDict):
208
"""A class for warning about keyword arguments that we use as
209
overrides in a Builder call.
211
This class exists to handle the fact that a single Builder call
212
can actually invoke multiple builders. This class only emits the
213
warnings once, no matter how many Builders are invoked.
215
def __init__(self, dict):
216
UserDict.UserDict.__init__(self, dict)
217
if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
218
self.already_warned = None
220
if self.already_warned:
222
for k in self.keys():
223
if misleading_keywords.has_key(k):
224
alt = misleading_keywords[k]
225
msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
226
SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
227
self.already_warned = 1
230
"""A factory for builder objects."""
232
if kw.has_key('generator'):
233
if kw.has_key('action'):
234
raise UserError, "You must not specify both an action and a generator."
235
kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'])
237
elif kw.has_key('action'):
238
source_ext_match = kw.get('source_ext_match', 1)
239
if kw.has_key('source_ext_match'):
240
del kw['source_ext_match']
241
if SCons.Util.is_Dict(kw['action']):
242
composite = DictCmdGenerator(kw['action'], source_ext_match)
243
kw['action'] = SCons.Action.CommandGeneratorAction(composite)
244
kw['src_suffix'] = composite.src_suffixes()
246
kw['action'] = SCons.Action.Action(kw['action'])
248
if kw.has_key('emitter'):
249
emitter = kw['emitter']
250
if SCons.Util.is_String(emitter):
251
# This allows users to pass in an Environment
252
# variable reference (like "$FOO") as an emitter.
253
# We will look in that Environment variable for
254
# a callable to use as the actual emitter.
255
var = SCons.Util.get_environment_var(emitter)
257
raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
258
kw['emitter'] = EmitterProxy(var)
259
elif SCons.Util.is_Dict(emitter):
260
kw['emitter'] = DictEmitter(emitter)
261
elif SCons.Util.is_List(emitter):
262
kw['emitter'] = ListEmitter(emitter)
264
result = apply(BuilderBase, (), kw)
266
if not composite is None:
267
result = CompositeBuilder(result, composite)
271
def _node_errors(builder, env, tlist, slist):
272
"""Validate that the lists of target and source nodes are
273
legal for this builder and environment. Raise errors or
274
issue warnings as appropriate.
277
# First, figure out if there are any errors in the way the targets
281
raise UserError, "Multiple ways to build the same target were specified for: %s" % t
282
if t.has_explicit_builder():
283
if not t.env is None and not t.env is env:
284
action = t.builder.action
285
t_contents = action.get_contents(tlist, slist, t.env)
286
contents = action.get_contents(tlist, slist, env)
288
if t_contents == contents:
289
msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
290
SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
292
msg = "Two environments with different actions were specified for the same target: %s" % t
295
if t.builder != builder:
296
msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
298
if t.get_executor().targets != tlist:
299
msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tlist))
301
elif t.sources != slist:
302
msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist))
305
if builder.single_source:
307
raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
310
"""This is a callable class that can act as a
311
Builder emitter. It holds on to a string that
312
is a key into an Environment dictionary, and will
313
look there at actual build time to see if it holds
314
a callable. If so, we will call that as the actual
316
def __init__(self, var):
317
self.var = SCons.Util.to_String(var)
319
def __call__(self, target, source, env):
322
# Recursively substitute the variable.
323
# We can't use env.subst() because it deals only
324
# in strings. Maybe we should change that?
325
while SCons.Util.is_String(emitter) and env.has_key(emitter):
326
emitter = env[emitter]
327
if callable(emitter):
328
target, source = emitter(target, source, env)
329
elif SCons.Util.is_List(emitter):
331
target, source = e(target, source, env)
333
return (target, source)
336
def __cmp__(self, other):
337
return cmp(self.var, other.var)
340
"""Base class for Builders, objects that create output
341
nodes (files) from input nodes (files).
344
if SCons.Memoize.use_memoizer:
345
__metaclass__ = SCons.Memoize.Memoized_Metaclass
347
memoizer_counters = []
349
def __init__(self, action = None,
353
target_factory = None,
354
source_factory = None,
355
target_scanner = None,
356
source_scanner = None,
365
ensure_suffix = False,
367
if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
371
if SCons.Util.is_Dict(prefix):
372
prefix = CallableSelector(prefix)
374
if SCons.Util.is_Dict(suffix):
375
suffix = CallableSelector(suffix)
377
self.single_source = single_source
378
if overrides.has_key('overrides'):
379
SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
380
"The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
381
"\tspecify the items as keyword arguments to the Builder() call instead.")
382
overrides.update(overrides['overrides'])
383
del overrides['overrides']
384
if overrides.has_key('scanner'):
385
SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
386
"The \"scanner\" keyword to Builder() creation has been deprecated;\n"
387
"\tuse: source_scanner or target_scanner as appropriate.")
388
del overrides['scanner']
389
self.overrides = overrides
391
self.set_suffix(suffix)
392
self.set_src_suffix(src_suffix)
393
self.ensure_suffix = ensure_suffix
395
self.target_factory = target_factory
396
self.source_factory = source_factory
397
self.target_scanner = target_scanner
398
self.source_scanner = source_scanner
400
self.emitter = emitter
402
# Optional Builder name should only be used for Builders
403
# that don't get attached to construction environments.
406
self.executor_kw = {}
407
if not chdir is _null:
408
self.executor_kw['chdir'] = chdir
409
self.is_explicit = is_explicit
411
if src_builder is None:
413
elif not SCons.Util.is_List(src_builder):
414
src_builder = [ src_builder ]
415
self.src_builder = src_builder
417
def __nonzero__(self):
418
raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
420
def get_name(self, env):
421
"""Attempts to get the name of the Builder.
423
Look at the BUILDERS variable of env, expecting it to be a
424
dictionary containing this Builder, and return the key of the
425
dictionary. If there's no key, then return a directly-configured
426
name (if there is one) or the name of the class (by default)."""
429
index = env['BUILDERS'].values().index(self)
430
return env['BUILDERS'].keys()[index]
431
except (AttributeError, KeyError, TypeError, ValueError):
434
except AttributeError:
435
return str(self.__class__)
437
def __cmp__(self, other):
438
return cmp(self.__dict__, other.__dict__)
440
def splitext(self, path, env=None):
444
matchsuf = filter(lambda S,path=path: path[-len(S):] == S,
445
self.src_suffixes(env))
447
suf = max(map(None, map(len, matchsuf), matchsuf))[1]
448
return [path[:-len(suf)], path[-len(suf):]]
449
return SCons.Util.splitext(path)
451
def get_single_executor(self, env, tlist, slist, executor_kw):
453
raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist))
454
return self.action.get_executor(env or self.env,
455
[], # env already has overrides
460
def get_multi_executor(self, env, tlist, slist, executor_kw):
462
executor = tlist[0].get_executor(create = 0)
463
except (AttributeError, IndexError):
464
return self.get_single_executor(env, tlist, slist, executor_kw)
466
executor.add_sources(slist)
469
def _adjustixes(self, files, pre, suf, ensure_suffix=False):
473
if not SCons.Util.is_List(files):
477
if SCons.Util.is_String(f):
478
f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
482
def _create_nodes(self, env, target = None, source = None):
483
"""Create and return lists of target and source nodes.
485
src_suf = self.get_src_suffix(env)
487
target_factory = env.get_factory(self.target_factory)
488
source_factory = env.get_factory(self.source_factory)
490
source = self._adjustixes(source, None, src_suf)
491
slist = env.arg2nodes(source, source_factory)
493
pre = self.get_prefix(env, slist)
494
suf = self.get_suffix(env, slist)
498
t_from_s = slist[0].target_from_source
499
except AttributeError:
500
raise UserError("Do not know how to create a target from source `%s'" % slist[0])
504
splitext = lambda S,self=self,env=env: self.splitext(S,env)
505
tlist = [ t_from_s(pre, suf, splitext) ]
507
target = self._adjustixes(target, pre, suf, self.ensure_suffix)
508
tlist = env.arg2nodes(target, target_factory, target=target, source=source)
511
# The emitter is going to do str(node), but because we're
512
# being called *from* a builder invocation, the new targets
513
# don't yet have a builder set on them and will look like
514
# source files. Fool the emitter's str() calls by setting
515
# up a temporary builder on the new targets.
518
if not t.is_derived():
520
new_targets.append(t)
522
orig_tlist = tlist[:]
523
orig_slist = slist[:]
525
target, source = self.emitter(target=tlist, source=slist, env=env)
527
# Now delete the temporary builders that we attached to any
528
# new targets, so that _node_errors() doesn't do weird stuff
529
# to them because it thinks they already have builders.
530
for t in new_targets:
531
if t.builder is self:
532
# Only delete the temporary builder if the emitter
533
# didn't change it on us.
536
# Have to call arg2nodes yet again, since it is legal for
537
# emitters to spit out strings as well as Node instances.
538
tlist = env.arg2nodes(target, target_factory,
539
target=orig_tlist, source=orig_slist)
540
slist = env.arg2nodes(source, source_factory,
541
target=orig_tlist, source=orig_slist)
545
def _execute(self, env, target, source, overwarn={}, executor_kw={}):
546
# We now assume that target and source are lists or None.
548
source = self.src_builder_sources(env, source, overwarn)
550
if self.single_source and len(source) > 1 and target is None:
552
if target is None: target = [None]*len(source)
553
for tgt, src in zip(target, source):
554
if not tgt is None: tgt = [tgt]
555
if not src is None: src = [src]
556
result.extend(self._execute(env, tgt, src, overwarn))
557
return SCons.Node.NodeList(result)
561
tlist, slist = self._create_nodes(env, target, source)
563
# Check for errors with the specified target/source lists.
564
_node_errors(self, env, tlist, slist)
566
# The targets are fine, so find or make the appropriate Executor to
567
# build this particular list of targets from this particular list of
570
get_executor = self.get_multi_executor
572
get_executor = self.get_single_executor
573
executor = get_executor(env, tlist, slist, executor_kw)
575
# Now set up the relevant information in the target Nodes themselves.
577
t.cwd = env.fs.getcwd()
581
t.set_executor(executor)
582
t.set_explicit(self.is_explicit)
584
return SCons.Node.NodeList(tlist)
586
def __call__(self, env, target=None, source=None, chdir=_null, **kw):
587
# We now assume that target and source are lists or None.
588
# The caller (typically Environment.BuilderWrapper) is
589
# responsible for converting any scalar values to lists.
591
ekw = self.executor_kw
593
ekw = self.executor_kw.copy()
596
if kw.has_key('srcdir'):
597
def prependDirIfRelative(f, srcdir=kw['srcdir']):
599
if SCons.Util.is_String(f) and not os.path.isabs(f):
600
f = os.path.join(srcdir, f)
602
if not SCons.Util.is_List(source):
604
source = map(prependDirIfRelative, source)
607
env_kw = self.overrides.copy()
612
env_kw = self.overrides
613
env = env.Override(env_kw)
614
return self._execute(env, target, source, OverrideWarner(kw), ekw)
616
def adjust_suffix(self, suff):
617
if suff and not suff[0] in [ '.', '_', '$' ]:
621
def get_prefix(self, env, sources=[]):
624
prefix = prefix(env, sources)
625
return env.subst(prefix)
627
def set_suffix(self, suffix):
628
if not callable(suffix):
629
suffix = self.adjust_suffix(suffix)
632
def get_suffix(self, env, sources=[]):
635
suffix = suffix(env, sources)
636
return env.subst(suffix)
638
def set_src_suffix(self, src_suffix):
641
elif not SCons.Util.is_List(src_suffix):
642
src_suffix = [ src_suffix ]
643
adjust = lambda suf, s=self: \
644
callable(suf) and suf or s.adjust_suffix(suf)
645
self.src_suffix = map(adjust, src_suffix)
647
def get_src_suffix(self, env):
648
"""Get the first src_suffix in the list of src_suffixes."""
649
ret = self.src_suffixes(env)
654
def add_emitter(self, suffix, emitter):
655
"""Add a suffix-emitter mapping to this Builder.
657
This assumes that emitter has been initialized with an
658
appropriate dictionary type, and will throw a TypeError if
659
not, so the caller is responsible for knowing that this is an
660
appropriate method to call for the Builder in question.
662
self.emitter[suffix] = emitter
664
def add_src_builder(self, builder):
666
Add a new Builder to the list of src_builders.
668
This requires wiping out cached values so that the computed
669
lists of source suffixes get re-calculated.
672
self.src_builder.append(builder)
674
def _get_sdict(self, env):
676
Returns a dictionary mapping all of the source suffixes of all
677
src_builders of this Builder to the underlying Builder that
678
should be called first.
680
This dictionary is used for each target specified, so we save a
681
lot of extra computation by memoizing it for each construction
684
Note that this is re-computed each time, not cached, because there
685
might be changes to one of our source Builders (or one of their
686
source Builders, and so on, and so on...) that we can't "see."
688
The underlying methods we call cache their computed values,
689
though, so we hope repeatedly aggregating them into a dictionary
690
like this won't be too big a hit. We may need to look for a
691
better way to do this if performance data show this has turned
692
into a significant bottleneck.
695
for bld in self.get_src_builders(env):
696
for suf in bld.src_suffixes(env):
700
def src_builder_sources(self, env, source, overwarn={}):
701
sdict = self._get_sdict(env)
703
src_suffixes = self.src_suffixes(env)
705
lengths = list(set(map(len, src_suffixes)))
707
def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
708
node_suffixes = map(lambda l, n=name: n[-l:], lengths)
709
for suf in src_suffixes:
710
if suf in node_suffixes:
715
for s in SCons.Util.flatten(source):
716
if SCons.Util.is_String(s):
717
match_suffix = match_src_suffix(env.subst(s))
718
if not match_suffix and not '.' in s:
719
src_suf = self.get_src_suffix(env)
720
s = self._adjustixes(s, None, src_suf)[0]
722
match_suffix = match_src_suffix(s.name)
725
bld = sdict[match_suffix]
729
tlist = bld._execute(env, None, [s], overwarn)
730
# If the subsidiary Builder returned more than one
731
# target, then filter out any sources that this
732
# Builder isn't capable of building.
734
mss = lambda t, m=match_src_suffix: m(t.name)
735
tlist = filter(mss, tlist)
740
source_factory = env.get_factory(self.source_factory)
742
return env.arg2nodes(result, source_factory)
744
def _get_src_builders_key(self, env):
747
memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
749
def get_src_builders(self, env):
751
Returns the list of source Builders for this Builder.
753
This exists mainly to look up Builders referenced as
754
strings in the 'BUILDER' variable of the construction
755
environment and cache the result.
759
memo_dict = self._memo['get_src_builders']
762
self._memo['get_src_builders'] = memo_dict
765
return memo_dict[memo_key]
770
for bld in self.src_builder:
771
if SCons.Util.is_String(bld):
773
bld = env['BUILDERS'][bld]
778
memo_dict[memo_key] = builders
781
def _subst_src_suffixes_key(self, env):
784
memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
786
def subst_src_suffixes(self, env):
788
The suffix list may contain construction variable expansions,
789
so we have to evaluate the individual strings. To avoid doing
790
this over and over, we memoize the results for each construction
795
memo_dict = self._memo['subst_src_suffixes']
798
self._memo['subst_src_suffixes'] = memo_dict
801
return memo_dict[memo_key]
804
suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
805
memo_dict[memo_key] = suffixes
808
def src_suffixes(self, env):
810
Returns the list of source suffixes for all src_builders of this
813
This is essentially a recursive descent of the src_builder "tree."
814
(This value isn't cached because there may be changes in a
815
src_builder many levels deep that we can't see.)
818
suffixes = self.subst_src_suffixes(env)
821
for builder in self.get_src_builders(env):
822
for s in builder.src_suffixes(env):
823
if not sdict.has_key(s):
828
class CompositeBuilder(SCons.Util.Proxy):
829
"""A Builder Proxy whose main purpose is to always have
830
a DictCmdGenerator as its action, and to provide access
831
to the DictCmdGenerator's add_action() method.
834
def __init__(self, builder, cmdgen):
835
if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
836
SCons.Util.Proxy.__init__(self, builder)
838
# cmdgen should always be an instance of DictCmdGenerator.
840
self.builder = builder
842
def add_action(self, suffix, action):
843
self.cmdgen.add_action(suffix, action)
844
self.set_src_suffix(self.cmdgen.src_suffixes())