1 """
2 SCons.Builder
3
4 Builder object subsystem.
5
6 A Builder object is a callable that encapsulates information about how
7 to execute actions to create a target Node (file) from source Nodes
8 (files), and how to create those dependencies for tracking.
9
10 The main entry point here is the Builder() factory method. This provides
11 a procedural interface that creates the right underlying Builder object
12 based on the keyword arguments supplied and the types of the arguments.
13
14 The goal is for this external interface to be simple enough that the
15 vast majority of users can create new Builders as necessary to support
16 building new types of files in their configurations, without having to
17 dive any deeper into this subsystem.
18
19 The base class here is BuilderBase. This is a concrete base class which
20 does, in fact, represent the Builder objects that we (or users) create.
21
22 There is also a proxy that looks like a Builder:
23
24 CompositeBuilder
25
26 This proxies for a Builder with an action that is actually a
27 dictionary that knows how to map file suffixes to a specific
28 action. This is so that we can invoke different actions
29 (compilers, compile options) for different flavors of source
30 files.
31
32 Builders and their proxies have the following public interface methods
33 used by other modules:
34
35 - __call__()
36 THE public interface. Calling a Builder object (with the
37 use of internal helper methods) sets up the target and source
38 dependencies, appropriate mapping to a specific action, and the
39 environment manipulation necessary for overridden construction
40 variable. This also takes care of warning about possible mistakes
41 in keyword arguments.
42
43 - add_emitter()
44 Adds an emitter for a specific file suffix, used by some Tool
45 modules to specify that (for example) a yacc invocation on a .y
46 can create a .h *and* a .c file.
47
48 - add_action()
49 Adds an action for a specific file suffix, heavily used by
50 Tool modules to add their specific action(s) for turning
51 a source file into an object file to the global static
52 and shared object file Builders.
53
54 There are the following methods for internal use within this module:
55
56 - _execute()
57 The internal method that handles the heavily lifting when a
58 Builder is called. This is used so that the __call__() methods
59 can set up warning about possible mistakes in keyword-argument
60 overrides, and *then* execute all of the steps necessary so that
61 the warnings only occur once.
62
63 - get_name()
64 Returns the Builder's name within a specific Environment,
65 primarily used to try to return helpful information in error
66 messages.
67
68 - adjust_suffix()
69 - get_prefix()
70 - get_suffix()
71 - get_src_suffix()
72 - set_src_suffix()
73 Miscellaneous stuff for handling the prefix and suffix
74 manipulation we use in turning source file names into target
75 file names.
76
77 """
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 __revision__ = "src/engine/SCons/Builder.py 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan"
102
103 import collections
104
105 import SCons.Action
106 import SCons.Debug
107 from SCons.Debug import logInstanceCreation
108 from SCons.Errors import InternalError, UserError
109 import SCons.Executor
110 import SCons.Memoize
111 import SCons.Util
112 import SCons.Warnings
116
117 _null = _Null
118
119 -def match_splitext(path, suffixes = []):
120 if suffixes:
121 matchsuf = [S for S in suffixes if path[-len(S):] == S]
122 if matchsuf:
123 suf = max([(len(_f),_f) for _f in matchsuf])[1]
124 return [path[:-len(suf)], path[-len(suf):]]
125 return SCons.Util.splitext(path)
126
128 """This is a callable class that can be used as a
129 command generator function. It holds on to a dictionary
130 mapping file suffixes to Actions. It uses that dictionary
131 to return the proper action based on the file suffix of
132 the source file."""
133
134 - def __init__(self, dict=None, source_ext_match=1):
137
139 return list(self.keys())
140
142 """Add a suffix-action pair to the mapping.
143 """
144 self[suffix] = action
145
146 - def __call__(self, target, source, env, for_signature):
147 if not source:
148 return []
149
150 if self.source_ext_match:
151 suffixes = self.src_suffixes()
152 ext = None
153 for src in map(str, source):
154 my_ext = match_splitext(src, suffixes)[1]
155 if ext and my_ext != ext:
156 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s"
157 % (repr(list(map(str, target))), src, ext, my_ext))
158 ext = my_ext
159 else:
160 ext = match_splitext(str(source[0]), self.src_suffixes())[1]
161
162 if not ext:
163
164 raise UserError("While building `%s': "
165 "Cannot deduce file extension from source files: %s"
166 % (repr(list(map(str, target))), repr(list(map(str, source)))))
167
168 try:
169 ret = SCons.Util.Selector.__call__(self, env, source, ext)
170 except KeyError as e:
171 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2]))
172 if ret is None:
173 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." % \
174 (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys()))))
175 return ret
176
178 """A callable dictionary that will, in turn, call the value it
179 finds if it can."""
185
187 """A callable dictionary that maps file suffixes to emitters.
188 When called, it finds the right emitter in its dictionary for the
189 suffix of the first source file, and calls that emitter to get the
190 right lists of targets and sources to return. If there's no emitter
191 for the suffix in its dictionary, the original target and source are
192 returned.
193 """
194 - def __call__(self, target, source, env):
195 emitter = SCons.Util.Selector.__call__(self, env, source)
196 if emitter:
197 target, source = emitter(target, source, env)
198 return (target, source)
199
201 """A callable list of emitters that calls each in sequence,
202 returning the result.
203 """
204 - def __call__(self, target, source, env):
205 for e in self.data:
206 target, source = e(target, source, env)
207 return (target, source)
208
209
210
211
212
213 misleading_keywords = {
214 'targets' : 'target',
215 'sources' : 'source',
216 }
219 """A class for warning about keyword arguments that we use as
220 overrides in a Builder call.
221
222 This class exists to handle the fact that a single Builder call
223 can actually invoke multiple builders. This class only emits the
224 warnings once, no matter how many Builders are invoked.
225 """
239
241 """A factory for builder objects."""
242 composite = None
243 if 'generator' in kw:
244 if 'action' in kw:
245 raise UserError("You must not specify both an action and a generator.")
246 kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
247 del kw['generator']
248 elif 'action' in kw:
249 source_ext_match = kw.get('source_ext_match', 1)
250 if 'source_ext_match' in kw:
251 del kw['source_ext_match']
252 if SCons.Util.is_Dict(kw['action']):
253 composite = DictCmdGenerator(kw['action'], source_ext_match)
254 kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
255 kw['src_suffix'] = composite.src_suffixes()
256 else:
257 kw['action'] = SCons.Action.Action(kw['action'])
258
259 if 'emitter' in kw:
260 emitter = kw['emitter']
261 if SCons.Util.is_String(emitter):
262
263
264
265
266 var = SCons.Util.get_environment_var(emitter)
267 if not var:
268 raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter)
269 kw['emitter'] = EmitterProxy(var)
270 elif SCons.Util.is_Dict(emitter):
271 kw['emitter'] = DictEmitter(emitter)
272 elif SCons.Util.is_List(emitter):
273 kw['emitter'] = ListEmitter(emitter)
274
275 result = BuilderBase(**kw)
276
277 if not composite is None:
278 result = CompositeBuilder(result, composite)
279
280 return result
281
283 """Validate that the lists of target and source nodes are
284 legal for this builder and environment. Raise errors or
285 issue warnings as appropriate.
286 """
287
288
289
290 for t in tlist:
291 if t.side_effect:
292 raise UserError("Multiple ways to build the same target were specified for: %s" % t)
293 if t.has_explicit_builder():
294
295
296 if (not t.env is None and not t.env is env and
297
298
299 not (getattr(t.env, '__subject', 0) is getattr(env, '__subject', 1) and
300 getattr(t.env, 'overrides', 0) == getattr(env, 'overrides', 1) and
301 not builder.multi)):
302 action = t.builder.action
303 t_contents = t.builder.action.get_contents(tlist, slist, t.env)
304 contents = builder.action.get_contents(tlist, slist, env)
305
306 if t_contents == contents:
307 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))
308 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
309 else:
310 try:
311 msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8'))
312 except UnicodeDecodeError as e:
313 msg = "Two environments with different actions were specified for the same target: %s"%t
314 raise UserError(msg)
315 if builder.multi:
316 if t.builder != builder:
317 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
318 raise UserError(msg)
319
320 if t.get_executor().get_all_targets() != tlist:
321 msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist)))
322 raise UserError(msg)
323 elif t.sources != slist:
324 msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist)))
325 raise UserError(msg)
326
327 if builder.single_source:
328 if len(slist) > 1:
329 raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
330
332 """This is a callable class that can act as a
333 Builder emitter. It holds on to a string that
334 is a key into an Environment dictionary, and will
335 look there at actual build time to see if it holds
336 a callable. If so, we will call that as the actual
337 emitter."""
340
341 - def __call__(self, target, source, env):
342 emitter = self.var
343
344
345
346
347 while SCons.Util.is_String(emitter) and emitter in env:
348 emitter = env[emitter]
349 if callable(emitter):
350 target, source = emitter(target, source, env)
351 elif SCons.Util.is_List(emitter):
352 for e in emitter:
353 target, source = e(target, source, env)
354
355 return (target, source)
356
357
359 return self.var == other.var
360
362 return self.var < other.var
363
365 """Base class for Builders, objects that create output
366 nodes (files) from input nodes (files).
367 """
368
369 - def __init__(self, action = None,
370 prefix = '',
371 suffix = '',
372 src_suffix = '',
373 target_factory = None,
374 source_factory = None,
375 target_scanner = None,
376 source_scanner = None,
377 emitter = None,
378 multi = 0,
379 env = None,
380 single_source = 0,
381 name = None,
382 chdir = _null,
383 is_explicit = 1,
384 src_builder = None,
385 ensure_suffix = False,
386 **overrides):
436
438 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
439
442
444 """Attempts to get the name of the Builder.
445
446 Look at the BUILDERS variable of env, expecting it to be a
447 dictionary containing this Builder, and return the key of the
448 dictionary. If there's no key, then return a directly-configured
449 name (if there is one) or the name of the class (by default)."""
450
451 try:
452 index = list(env['BUILDERS'].values()).index(self)
453 return list(env['BUILDERS'].keys())[index]
454 except (AttributeError, KeyError, TypeError, ValueError):
455 try:
456 return self.name
457 except AttributeError:
458 return str(self.__class__)
459
461 return self.__dict__ == other.__dict__
462
463 - def splitext(self, path, env=None):
464 if not env:
465 env = self.env
466 if env:
467 suffixes = self.src_suffixes(env)
468 else:
469 suffixes = []
470 return match_splitext(path, suffixes)
471
472 - def _adjustixes(self, files, pre, suf, ensure_suffix=False):
473 if not files:
474 return []
475 result = []
476 if not SCons.Util.is_List(files):
477 files = [files]
478
479 for f in files:
480 if SCons.Util.is_String(f):
481 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
482 result.append(f)
483 return result
484
486 """Create and return lists of target and source nodes.
487 """
488 src_suf = self.get_src_suffix(env)
489
490 target_factory = env.get_factory(self.target_factory)
491 source_factory = env.get_factory(self.source_factory)
492
493 source = self._adjustixes(source, None, src_suf)
494 slist = env.arg2nodes(source, source_factory)
495
496 pre = self.get_prefix(env, slist)
497 suf = self.get_suffix(env, slist)
498
499 if target is None:
500 try:
501 t_from_s = slist[0].target_from_source
502 except AttributeError:
503 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
504 except IndexError:
505 tlist = []
506 else:
507 splitext = lambda S: self.splitext(S,env)
508 tlist = [ t_from_s(pre, suf, splitext) ]
509 else:
510 target = self._adjustixes(target, pre, suf, self.ensure_suffix)
511 tlist = env.arg2nodes(target, target_factory, target=target, source=source)
512
513 if self.emitter:
514
515
516
517
518
519 new_targets = []
520 for t in tlist:
521 if not t.is_derived():
522 t.builder_set(self)
523 new_targets.append(t)
524
525 orig_tlist = tlist[:]
526 orig_slist = slist[:]
527
528 target, source = self.emitter(target=tlist, source=slist, env=env)
529
530
531
532
533 for t in new_targets:
534 if t.builder is self:
535
536
537 t.builder_set(None)
538
539
540
541 tlist = env.arg2nodes(target, target_factory,
542 target=orig_tlist, source=orig_slist)
543 slist = env.arg2nodes(source, source_factory,
544 target=orig_tlist, source=orig_slist)
545
546 return tlist, slist
547
548 - def _execute(self, env, target, source, overwarn={}, executor_kw={}):
549
550 if self.src_builder:
551 source = self.src_builder_sources(env, source, overwarn)
552
553 if self.single_source and len(source) > 1 and target is None:
554 result = []
555 if target is None: target = [None]*len(source)
556 for tgt, src in zip(target, source):
557 if not tgt is None: tgt = [tgt]
558 if not src is None: src = [src]
559 result.extend(self._execute(env, tgt, src, overwarn))
560 return SCons.Node.NodeList(result)
561
562 overwarn.warn()
563
564 tlist, slist = self._create_nodes(env, target, source)
565
566
567 _node_errors(self, env, tlist, slist)
568
569
570
571
572
573 executor = None
574 key = None
575
576 if self.multi:
577 try:
578 executor = tlist[0].get_executor(create = 0)
579 except (AttributeError, IndexError):
580 pass
581 else:
582 executor.add_sources(slist)
583
584 if executor is None:
585 if not self.action:
586 fmt = "Builder %s must have an action to build %s."
587 raise UserError(fmt % (self.get_name(env or self.env),
588 list(map(str,tlist))))
589 key = self.action.batch_key(env or self.env, tlist, slist)
590 if key:
591 try:
592 executor = SCons.Executor.GetBatchExecutor(key)
593 except KeyError:
594 pass
595 else:
596 executor.add_batch(tlist, slist)
597
598 if executor is None:
599 executor = SCons.Executor.Executor(self.action, env, [],
600 tlist, slist, executor_kw)
601 if key:
602 SCons.Executor.AddBatchExecutor(key, executor)
603
604
605 for t in tlist:
606 t.cwd = env.fs.getcwd()
607 t.builder_set(self)
608 t.env_set(env)
609 t.add_source(slist)
610 t.set_executor(executor)
611 t.set_explicit(self.is_explicit)
612
613 return SCons.Node.NodeList(tlist)
614
615 - def __call__(self, env, target=None, source=None, chdir=_null, **kw):
616
617
618
619 if chdir is _null:
620 ekw = self.executor_kw
621 else:
622 ekw = self.executor_kw.copy()
623 ekw['chdir'] = chdir
624 if 'chdir' in ekw and SCons.Util.is_String(ekw['chdir']):
625 ekw['chdir'] = env.subst(ekw['chdir'])
626 if kw:
627 if 'srcdir' in kw:
628 def prependDirIfRelative(f, srcdir=kw['srcdir']):
629 import os.path
630 if SCons.Util.is_String(f) and not os.path.isabs(f):
631 f = os.path.join(srcdir, f)
632 return f
633 if not SCons.Util.is_List(source):
634 source = [source]
635 source = list(map(prependDirIfRelative, source))
636 del kw['srcdir']
637 if self.overrides:
638 env_kw = self.overrides.copy()
639 env_kw.update(kw)
640 else:
641 env_kw = kw
642 else:
643 env_kw = self.overrides
644 env = env.Override(env_kw)
645 return self._execute(env, target, source, OverrideWarner(kw), ekw)
646
648 if suff and not suff[0] in [ '.', '_', '$' ]:
649 return '.' + suff
650 return suff
651
653 prefix = self.prefix
654 if callable(prefix):
655 prefix = prefix(env, sources)
656 return env.subst(prefix)
657
659 if not callable(suffix):
660 suffix = self.adjust_suffix(suffix)
661 self.suffix = suffix
662
664 suffix = self.suffix
665 if callable(suffix):
666 suffix = suffix(env, sources)
667 return env.subst(suffix)
668
670 if not src_suffix:
671 src_suffix = []
672 elif not SCons.Util.is_List(src_suffix):
673 src_suffix = [ src_suffix ]
674 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
675
677 """Get the first src_suffix in the list of src_suffixes."""
678 ret = self.src_suffixes(env)
679 if not ret:
680 return ''
681 return ret[0]
682
684 """Add a suffix-emitter mapping to this Builder.
685
686 This assumes that emitter has been initialized with an
687 appropriate dictionary type, and will throw a TypeError if
688 not, so the caller is responsible for knowing that this is an
689 appropriate method to call for the Builder in question.
690 """
691 self.emitter[suffix] = emitter
692
694 """
695 Add a new Builder to the list of src_builders.
696
697 This requires wiping out cached values so that the computed
698 lists of source suffixes get re-calculated.
699 """
700 self._memo = {}
701 self.src_builder.append(builder)
702
704 """
705 Returns a dictionary mapping all of the source suffixes of all
706 src_builders of this Builder to the underlying Builder that
707 should be called first.
708
709 This dictionary is used for each target specified, so we save a
710 lot of extra computation by memoizing it for each construction
711 environment.
712
713 Note that this is re-computed each time, not cached, because there
714 might be changes to one of our source Builders (or one of their
715 source Builders, and so on, and so on...) that we can't "see."
716
717 The underlying methods we call cache their computed values,
718 though, so we hope repeatedly aggregating them into a dictionary
719 like this won't be too big a hit. We may need to look for a
720 better way to do this if performance data show this has turned
721 into a significant bottleneck.
722 """
723 sdict = {}
724 for bld in self.get_src_builders(env):
725 for suf in bld.src_suffixes(env):
726 sdict[suf] = bld
727 return sdict
728
742
743 result = []
744 for s in SCons.Util.flatten(source):
745 if SCons.Util.is_String(s):
746 match_suffix = match_src_suffix(env.subst(s))
747 if not match_suffix and not '.' in s:
748 src_suf = self.get_src_suffix(env)
749 s = self._adjustixes(s, None, src_suf)[0]
750 else:
751 match_suffix = match_src_suffix(s.name)
752 if match_suffix:
753 try:
754 bld = sdict[match_suffix]
755 except KeyError:
756 result.append(s)
757 else:
758 tlist = bld._execute(env, None, [s], overwarn)
759
760
761
762 if len(tlist) > 1:
763 tlist = [t for t in tlist if match_src_suffix(t.name)]
764 result.extend(tlist)
765 else:
766 result.append(s)
767
768 source_factory = env.get_factory(self.source_factory)
769
770 return env.arg2nodes(result, source_factory)
771
774
775 @SCons.Memoize.CountDictCall(_get_src_builders_key)
777 """
778 Returns the list of source Builders for this Builder.
779
780 This exists mainly to look up Builders referenced as
781 strings in the 'BUILDER' variable of the construction
782 environment and cache the result.
783 """
784 memo_key = id(env)
785 try:
786 memo_dict = self._memo['get_src_builders']
787 except KeyError:
788 memo_dict = {}
789 self._memo['get_src_builders'] = memo_dict
790 else:
791 try:
792 return memo_dict[memo_key]
793 except KeyError:
794 pass
795
796 builders = []
797 for bld in self.src_builder:
798 if SCons.Util.is_String(bld):
799 try:
800 bld = env['BUILDERS'][bld]
801 except KeyError:
802 continue
803 builders.append(bld)
804
805 memo_dict[memo_key] = builders
806 return builders
807
810
811 @SCons.Memoize.CountDictCall(_subst_src_suffixes_key)
813 """
814 The suffix list may contain construction variable expansions,
815 so we have to evaluate the individual strings. To avoid doing
816 this over and over, we memoize the results for each construction
817 environment.
818 """
819 memo_key = id(env)
820 try:
821 memo_dict = self._memo['subst_src_suffixes']
822 except KeyError:
823 memo_dict = {}
824 self._memo['subst_src_suffixes'] = memo_dict
825 else:
826 try:
827 return memo_dict[memo_key]
828 except KeyError:
829 pass
830 suffixes = [env.subst(x) for x in self.src_suffix]
831 memo_dict[memo_key] = suffixes
832 return suffixes
833
835 """
836 Returns the list of source suffixes for all src_builders of this
837 Builder.
838
839 This is essentially a recursive descent of the src_builder "tree."
840 (This value isn't cached because there may be changes in a
841 src_builder many levels deep that we can't see.)
842 """
843 sdict = {}
844 suffixes = self.subst_src_suffixes(env)
845 for s in suffixes:
846 sdict[s] = 1
847 for builder in self.get_src_builders(env):
848 for s in builder.src_suffixes(env):
849 if s not in sdict:
850 sdict[s] = 1
851 suffixes.append(s)
852 return suffixes
853
855 """A Builder Proxy whose main purpose is to always have
856 a DictCmdGenerator as its action, and to provide access
857 to the DictCmdGenerator's add_action() method.
858 """
859
867
868 __call__ = SCons.Util.Delegate('__call__')
869
873
875 """"Returns True if the specified obj is one of our Builder classes.
876
877 The test is complicated a bit by the fact that CompositeBuilder
878 is a proxy, not a subclass of BuilderBase.
879 """
880 return (isinstance(obj, BuilderBase)
881 or isinstance(obj, CompositeBuilder)
882 or callable(obj))
883
884
885
886
887
888
889