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
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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
109 from SCons.Debug import logInstanceCreation
110 from SCons.Errors import InternalError, UserError
111 import SCons.Executor
116 import SCons.Warnings
123 class DictCmdGenerator(SCons.Util.Selector):
124 """This is a callable class that can be used as a
125 command generator function. It holds on to a dictionary
126 mapping file suffixes to Actions. It uses that dictionary
127 to return the proper action based on the file suffix of
130 def __init__(self, dict=None, source_ext_match=1):
131 SCons.Util.Selector.__init__(self, dict)
132 self.source_ext_match = source_ext_match
134 def src_suffixes(self):
137 def add_action(self, suffix, action):
138 """Add a suffix-action pair to the mapping.
140 self[suffix] = action
142 def __call__(self, target, source, env, for_signature):
146 if self.source_ext_match:
148 for src in map(str, source):
149 my_ext = SCons.Util.splitext(src)[1]
150 if ext and my_ext != ext:
151 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
154 ext = SCons.Util.splitext(str(source[0]))[1]
157 raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
160 ret = SCons.Util.Selector.__call__(self, env, source)
162 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
164 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." % \
165 (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys())))
168 class CallableSelector(SCons.Util.Selector):
169 """A callable dictionary that will, in turn, call the value it
171 def __call__(self, env, source):
172 value = SCons.Util.Selector.__call__(self, env, source)
174 value = value(env, source)
177 class DictEmitter(SCons.Util.Selector):
178 """A callable dictionary that maps file suffixes to emitters.
179 When called, it finds the right emitter in its dictionary for the
180 suffix of the first source file, and calls that emitter to get the
181 right lists of targets and sources to return. If there's no emitter
182 for the suffix in its dictionary, the original target and source are
185 def __call__(self, target, source, env):
186 emitter = SCons.Util.Selector.__call__(self, env, source)
188 target, source = emitter(target, source, env)
189 return (target, source)
191 class ListEmitter(UserList.UserList):
192 """A callable list of emitters that calls each in sequence,
193 returning the result.
195 def __call__(self, target, source, env):
197 target, source = e(target, source, env)
198 return (target, source)
200 # These are a common errors when calling a Builder;
201 # they are similar to the 'target' and 'source' keyword args to builders,
202 # so we issue warnings when we see them. The warnings can, of course,
204 misleading_keywords = {
205 'targets' : 'target',
206 'sources' : 'source',
209 class OverrideWarner(UserDict.UserDict):
210 """A class for warning about keyword arguments that we use as
211 overrides in a Builder call.
213 This class exists to handle the fact that a single Builder call
214 can actually invoke multiple builders. This class only emits the
215 warnings once, no matter how many Builders are invoked.
217 def __init__(self, dict):
218 UserDict.UserDict.__init__(self, dict)
219 if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
220 self.already_warned = None
222 if self.already_warned:
224 for k in self.keys():
225 if misleading_keywords.has_key(k):
226 alt = misleading_keywords[k]
227 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
228 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
229 self.already_warned = 1
232 """A factory for builder objects."""
234 if kw.has_key('generator'):
235 if kw.has_key('action'):
236 raise UserError, "You must not specify both an action and a generator."
237 kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'])
239 elif kw.has_key('action'):
240 source_ext_match = kw.get('source_ext_match', 1)
241 if kw.has_key('source_ext_match'):
242 del kw['source_ext_match']
243 if SCons.Util.is_Dict(kw['action']):
244 composite = DictCmdGenerator(kw['action'], source_ext_match)
245 kw['action'] = SCons.Action.CommandGeneratorAction(composite)
246 kw['src_suffix'] = composite.src_suffixes()
248 kw['action'] = SCons.Action.Action(kw['action'])
250 if kw.has_key('emitter'):
251 emitter = kw['emitter']
252 if SCons.Util.is_String(emitter):
253 # This allows users to pass in an Environment
254 # variable reference (like "$FOO") as an emitter.
255 # We will look in that Environment variable for
256 # a callable to use as the actual emitter.
257 var = SCons.Util.get_environment_var(emitter)
259 raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
260 kw['emitter'] = EmitterProxy(var)
261 elif SCons.Util.is_Dict(emitter):
262 kw['emitter'] = DictEmitter(emitter)
263 elif SCons.Util.is_List(emitter):
264 kw['emitter'] = ListEmitter(emitter)
266 result = apply(BuilderBase, (), kw)
268 if not composite is None:
269 result = CompositeBuilder(result, composite)
273 def _node_errors(builder, env, tlist, slist):
274 """Validate that the lists of target and source nodes are
275 legal for this builder and environment. Raise errors or
276 issue warnings as appropriate.
279 # First, figure out if there are any errors in the way the targets
283 raise UserError, "Multiple ways to build the same target were specified for: %s" % t
284 if t.has_explicit_builder():
285 if not t.env is None and not t.env is env:
286 action = t.builder.action
287 t_contents = action.get_contents(tlist, slist, t.env)
288 contents = action.get_contents(tlist, slist, env)
290 if t_contents == contents:
291 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))
292 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
294 msg = "Two environments with different actions were specified for the same target: %s" % t
297 if t.builder != builder:
298 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
300 if t.get_executor().targets != tlist:
301 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))
303 elif t.sources != slist:
304 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))
307 if builder.single_source:
309 raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
312 """This is a callable class that can act as a
313 Builder emitter. It holds on to a string that
314 is a key into an Environment dictionary, and will
315 look there at actual build time to see if it holds
316 a callable. If so, we will call that as the actual
318 def __init__(self, var):
319 self.var = SCons.Util.to_String(var)
321 def __call__(self, target, source, env):
324 # Recursively substitute the variable.
325 # We can't use env.subst() because it deals only
326 # in strings. Maybe we should change that?
327 while SCons.Util.is_String(emitter) and env.has_key(emitter):
328 emitter = env[emitter]
329 if callable(emitter):
330 target, source = emitter(target, source, env)
331 elif SCons.Util.is_List(emitter):
333 target, source = e(target, source, env)
335 return (target, source)
338 def __cmp__(self, other):
339 return cmp(self.var, other.var)
342 """Base class for Builders, objects that create output
343 nodes (files) from input nodes (files).
346 if SCons.Memoize.use_memoizer:
347 __metaclass__ = SCons.Memoize.Memoized_Metaclass
349 memoizer_counters = []
351 def __init__(self, action = None,
355 target_factory = None,
356 source_factory = None,
357 target_scanner = None,
358 source_scanner = None,
367 ensure_suffix = False,
369 if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
373 if SCons.Util.is_Dict(prefix):
374 prefix = CallableSelector(prefix)
376 if SCons.Util.is_Dict(suffix):
377 suffix = CallableSelector(suffix)
379 self.single_source = single_source
380 if overrides.has_key('overrides'):
381 SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
382 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
383 "\tspecify the items as keyword arguments to the Builder() call instead.")
384 overrides.update(overrides['overrides'])
385 del overrides['overrides']
386 if overrides.has_key('scanner'):
387 SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
388 "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
389 "\tuse: source_scanner or target_scanner as appropriate.")
390 del overrides['scanner']
391 self.overrides = overrides
393 self.set_suffix(suffix)
394 self.set_src_suffix(src_suffix)
395 self.ensure_suffix = ensure_suffix
397 self.target_factory = target_factory
398 self.source_factory = source_factory
399 self.target_scanner = target_scanner
400 self.source_scanner = source_scanner
402 self.emitter = emitter
404 # Optional Builder name should only be used for Builders
405 # that don't get attached to construction environments.
408 self.executor_kw = {}
409 if not chdir is _null:
410 self.executor_kw['chdir'] = chdir
411 self.is_explicit = is_explicit
413 if 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)
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:
716 if SCons.Util.is_List(source):
717 source = SCons.Util.flatten(source)
721 if SCons.Util.is_String(s):
722 match_suffix = match_src_suffix(s)
723 if not match_suffix and not '.' in s:
724 src_suf = self.get_src_suffix(env)
725 s = self._adjustixes(s, None, src_suf)[0]
727 match_suffix = match_src_suffix(s.name)
730 bld = sdict[match_suffix]
734 tlist = bld._execute(env, None, [s], overwarn)
735 # If the subsidiary Builder returned more than one
736 # target, then filter out any sources that this
737 # Builder isn't capable of building.
739 mss = lambda t, m=match_src_suffix: m(t.name)
740 tlist = filter(mss, tlist)
745 source_factory = env.get_factory(self.source_factory)
747 return env.arg2nodes(result, source_factory)
749 def _get_src_builders_key(self, env):
752 memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
754 def get_src_builders(self, env):
756 Returns the list of source Builders for this Builder.
758 This exists mainly to look up Builders referenced as
759 strings in the 'BUILDER' variable of the construction
760 environment and cache the result.
764 memo_dict = self._memo['get_src_builders']
767 self._memo['get_src_builders'] = memo_dict
770 return memo_dict[memo_key]
775 for bld in self.src_builder:
776 if SCons.Util.is_String(bld):
778 bld = env['BUILDERS'][bld]
783 memo_dict[memo_key] = builders
786 def _subst_src_suffixes_key(self, env):
789 memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
791 def subst_src_suffixes(self, env):
793 The suffix list may contain construction variable expansions,
794 so we have to evaluate the individual strings. To avoid doing
795 this over and over, we memoize the results for each construction
800 memo_dict = self._memo['subst_src_suffixes']
803 self._memo['subst_src_suffixes'] = memo_dict
806 return memo_dict[memo_key]
809 suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
810 memo_dict[memo_key] = suffixes
813 def src_suffixes(self, env):
815 Returns the list of source suffixes for all src_builders of this
818 This is essentially a recursive descent of the src_builder "tree."
819 (This value isn't cached because there may be changes in a
820 src_builder many levels deep that we can't see.)
823 suffixes = self.subst_src_suffixes(env)
826 for builder in self.get_src_builders(env):
827 for s in builder.src_suffixes(env):
828 if not sdict.has_key(s):
833 class CompositeBuilder(SCons.Util.Proxy):
834 """A Builder Proxy whose main purpose is to always have
835 a DictCmdGenerator as its action, and to provide access
836 to the DictCmdGenerator's add_action() method.
839 def __init__(self, builder, cmdgen):
840 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
841 SCons.Util.Proxy.__init__(self, builder)
843 # cmdgen should always be an instance of DictCmdGenerator.
845 self.builder = builder
847 def add_action(self, suffix, action):
848 self.cmdgen.add_action(suffix, action)
849 self.set_src_suffix(self.cmdgen.src_suffixes())