Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(994)

Side by Side Diff: scons-2.0.1/engine/SCons/Builder.py

Issue 6711079: Added an unmodified copy of SCons to third_party. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/third_party/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « scons-2.0.1/engine/SCons/Action.py ('k') | scons-2.0.1/engine/SCons/CacheDir.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 """SCons.Builder
2
3 Builder object subsystem.
4
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.
8
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.
12
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.
17
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.
20
21 There is also a proxy that looks like a Builder:
22
23 CompositeBuilder
24
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
29 files.
30
31 Builders and their proxies have the following public interface methods
32 used by other modules:
33
34 __call__()
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
40 in keyword arguments.
41
42 add_emitter()
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.
46
47 add_action()
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.
52
53 There are the following methods for internal use within this module:
54
55 _execute()
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.
61
62 get_name()
63 Returns the Builder's name within a specific Environment,
64 primarily used to try to return helpful information in error
65 messages.
66
67 adjust_suffix()
68 get_prefix()
69 get_suffix()
70 get_src_suffix()
71 set_src_suffix()
72 Miscellaneous stuff for handling the prefix and suffix
73 manipulation we use in turning source file names into target
74 file names.
75
76 """
77
78 #
79 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S Cons Foundation
80 #
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:
88 #
89 # The above copyright notice and this permission notice shall be included
90 # in all copies or substantial portions of the Software.
91 #
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.
99
100 __revision__ = "src/engine/SCons/Builder.py 5134 2010/08/16 23:02:40 bdeegan"
101
102 import collections
103
104 import SCons.Action
105 from SCons.Debug import logInstanceCreation
106 from SCons.Errors import InternalError, UserError
107 import SCons.Executor
108 import SCons.Memoize
109 import SCons.Node
110 import SCons.Node.FS
111 import SCons.Util
112 import SCons.Warnings
113
114 class _Null(object):
115 pass
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
127 class DictCmdGenerator(SCons.Util.Selector):
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):
135 SCons.Util.Selector.__init__(self, dict)
136 self.source_ext_match = source_ext_match
137
138 def src_suffixes(self):
139 return list(self.keys())
140
141 def add_action(self, suffix, action):
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 #return ext
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, 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 bu ild from a source file with suffix `%s'. Expected a suffix in this list: %s." % \
174 (repr(list(map(str, target))), repr(list(map(str, so urce))), ext, repr(list(self.keys()))))
175 return ret
176
177 class CallableSelector(SCons.Util.Selector):
178 """A callable dictionary that will, in turn, call the value it
179 finds if it can."""
180 def __call__(self, env, source):
181 value = SCons.Util.Selector.__call__(self, env, source)
182 if callable(value):
183 value = value(env, source)
184 return value
185
186 class DictEmitter(SCons.Util.Selector):
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
200 class ListEmitter(collections.UserList):
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 # These are a common errors when calling a Builder;
210 # they are similar to the 'target' and 'source' keyword args to builders,
211 # so we issue warnings when we see them. The warnings can, of course,
212 # be disabled.
213 misleading_keywords = {
214 'targets' : 'target',
215 'sources' : 'source',
216 }
217
218 class OverrideWarner(collections.UserDict):
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 """
226 def __init__(self, dict):
227 collections.UserDict.__init__(self, dict)
228 if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
229 self.already_warned = None
230 def warn(self):
231 if self.already_warned:
232 return
233 for k in self.keys():
234 if k in misleading_keywords:
235 alt = misleading_keywords[k]
236 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
237 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, ms g)
238 self.already_warned = 1
239
240 def Builder(**kw):
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 # This allows users to pass in an Environment
263 # variable reference (like "$FOO") as an emitter.
264 # We will look in that Environment variable for
265 # a callable to use as the actual emitter.
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
282 def _node_errors(builder, env, tlist, slist):
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 # First, figure out if there are any errors in the way the targets
289 # were specified.
290 for t in tlist:
291 if t.side_effect:
292 raise UserError("Multiple ways to build the same target were specifi ed for: %s" % t)
293 if t.has_explicit_builder():
294 if not t.env is None and not t.env is env:
295 action = t.builder.action
296 t_contents = action.get_contents(tlist, slist, t.env)
297 contents = action.get_contents(tlist, slist, env)
298
299 if t_contents == contents:
300 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))
301 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarni ng, msg)
302 else:
303 msg = "Two environments with different actions were specifie d for the same target: %s" % t
304 raise UserError(msg)
305 if builder.multi:
306 if t.builder != builder:
307 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
308 raise UserError(msg)
309 # TODO(batch): list constructed each time!
310 if t.get_executor().get_all_targets() != tlist:
311 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)))
312 raise UserError(msg)
313 elif t.sources != slist:
314 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, slis t)))
315 raise UserError(msg)
316
317 if builder.single_source:
318 if len(slist) > 1:
319 raise UserError("More than one source given for single-source builde r: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
320
321 class EmitterProxy(object):
322 """This is a callable class that can act as a
323 Builder emitter. It holds on to a string that
324 is a key into an Environment dictionary, and will
325 look there at actual build time to see if it holds
326 a callable. If so, we will call that as the actual
327 emitter."""
328 def __init__(self, var):
329 self.var = SCons.Util.to_String(var)
330
331 def __call__(self, target, source, env):
332 emitter = self.var
333
334 # Recursively substitute the variable.
335 # We can't use env.subst() because it deals only
336 # in strings. Maybe we should change that?
337 while SCons.Util.is_String(emitter) and emitter in env:
338 emitter = env[emitter]
339 if callable(emitter):
340 target, source = emitter(target, source, env)
341 elif SCons.Util.is_List(emitter):
342 for e in emitter:
343 target, source = e(target, source, env)
344
345 return (target, source)
346
347
348 def __cmp__(self, other):
349 return cmp(self.var, other.var)
350
351 class BuilderBase(object):
352 """Base class for Builders, objects that create output
353 nodes (files) from input nodes (files).
354 """
355
356 if SCons.Memoize.use_memoizer:
357 __metaclass__ = SCons.Memoize.Memoized_Metaclass
358
359 memoizer_counters = []
360
361 def __init__(self, action = None,
362 prefix = '',
363 suffix = '',
364 src_suffix = '',
365 target_factory = None,
366 source_factory = None,
367 target_scanner = None,
368 source_scanner = None,
369 emitter = None,
370 multi = 0,
371 env = None,
372 single_source = 0,
373 name = None,
374 chdir = _null,
375 is_explicit = 1,
376 src_builder = None,
377 ensure_suffix = False,
378 **overrides):
379 if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
380 self._memo = {}
381 self.action = action
382 self.multi = multi
383 if SCons.Util.is_Dict(prefix):
384 prefix = CallableSelector(prefix)
385 self.prefix = prefix
386 if SCons.Util.is_Dict(suffix):
387 suffix = CallableSelector(suffix)
388 self.env = env
389 self.single_source = single_source
390 if 'overrides' in overrides:
391 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning,
392 "The \"overrides\" keyword to Builder() creation has been deprec ated;\n" +\
393 "\tspecify the items as keyword arguments to the Builder() call instead.")
394 overrides.update(overrides['overrides'])
395 del overrides['overrides']
396 if 'scanner' in overrides:
397 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning,
398 "The \"scanner\" keyword to Builder() creation h as been deprecated;\n"
399 "\tuse: source_scanner or target_scanner as appr opriate.")
400 del overrides['scanner']
401 self.overrides = overrides
402
403 self.set_suffix(suffix)
404 self.set_src_suffix(src_suffix)
405 self.ensure_suffix = ensure_suffix
406
407 self.target_factory = target_factory
408 self.source_factory = source_factory
409 self.target_scanner = target_scanner
410 self.source_scanner = source_scanner
411
412 self.emitter = emitter
413
414 # Optional Builder name should only be used for Builders
415 # that don't get attached to construction environments.
416 if name:
417 self.name = name
418 self.executor_kw = {}
419 if not chdir is _null:
420 self.executor_kw['chdir'] = chdir
421 self.is_explicit = is_explicit
422
423 if src_builder is None:
424 src_builder = []
425 elif not SCons.Util.is_List(src_builder):
426 src_builder = [ src_builder ]
427 self.src_builder = src_builder
428
429 def __nonzero__(self):
430 raise InternalError("Do not test for the Node.builder attribute directly ; use Node.has_builder() instead")
431
432 def get_name(self, env):
433 """Attempts to get the name of the Builder.
434
435 Look at the BUILDERS variable of env, expecting it to be a
436 dictionary containing this Builder, and return the key of the
437 dictionary. If there's no key, then return a directly-configured
438 name (if there is one) or the name of the class (by default)."""
439
440 try:
441 index = list(env['BUILDERS'].values()).index(self)
442 return list(env['BUILDERS'].keys())[index]
443 except (AttributeError, KeyError, TypeError, ValueError):
444 try:
445 return self.name
446 except AttributeError:
447 return str(self.__class__)
448
449 def __cmp__(self, other):
450 return cmp(self.__dict__, other.__dict__)
451
452 def splitext(self, path, env=None):
453 if not env:
454 env = self.env
455 if env:
456 suffixes = self.src_suffixes(env)
457 else:
458 suffixes = []
459 return match_splitext(path, suffixes)
460
461 def _adjustixes(self, files, pre, suf, ensure_suffix=False):
462 if not files:
463 return []
464 result = []
465 if not SCons.Util.is_List(files):
466 files = [files]
467
468 for f in files:
469 if SCons.Util.is_String(f):
470 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
471 result.append(f)
472 return result
473
474 def _create_nodes(self, env, target = None, source = None):
475 """Create and return lists of target and source nodes.
476 """
477 src_suf = self.get_src_suffix(env)
478
479 target_factory = env.get_factory(self.target_factory)
480 source_factory = env.get_factory(self.source_factory)
481
482 source = self._adjustixes(source, None, src_suf)
483 slist = env.arg2nodes(source, source_factory)
484
485 pre = self.get_prefix(env, slist)
486 suf = self.get_suffix(env, slist)
487
488 if target is None:
489 try:
490 t_from_s = slist[0].target_from_source
491 except AttributeError:
492 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
493 except IndexError:
494 tlist = []
495 else:
496 splitext = lambda S: self.splitext(S,env)
497 tlist = [ t_from_s(pre, suf, splitext) ]
498 else:
499 target = self._adjustixes(target, pre, suf, self.ensure_suffix)
500 tlist = env.arg2nodes(target, target_factory, target=target, source= source)
501
502 if self.emitter:
503 # The emitter is going to do str(node), but because we're
504 # being called *from* a builder invocation, the new targets
505 # don't yet have a builder set on them and will look like
506 # source files. Fool the emitter's str() calls by setting
507 # up a temporary builder on the new targets.
508 new_targets = []
509 for t in tlist:
510 if not t.is_derived():
511 t.builder_set(self)
512 new_targets.append(t)
513
514 orig_tlist = tlist[:]
515 orig_slist = slist[:]
516
517 target, source = self.emitter(target=tlist, source=slist, env=env)
518
519 # Now delete the temporary builders that we attached to any
520 # new targets, so that _node_errors() doesn't do weird stuff
521 # to them because it thinks they already have builders.
522 for t in new_targets:
523 if t.builder is self:
524 # Only delete the temporary builder if the emitter
525 # didn't change it on us.
526 t.builder_set(None)
527
528 # Have to call arg2nodes yet again, since it is legal for
529 # emitters to spit out strings as well as Node instances.
530 tlist = env.arg2nodes(target, target_factory,
531 target=orig_tlist, source=orig_slist)
532 slist = env.arg2nodes(source, source_factory,
533 target=orig_tlist, source=orig_slist)
534
535 return tlist, slist
536
537 def _execute(self, env, target, source, overwarn={}, executor_kw={}):
538 # We now assume that target and source are lists or None.
539 if self.src_builder:
540 source = self.src_builder_sources(env, source, overwarn)
541
542 if self.single_source and len(source) > 1 and target is None:
543 result = []
544 if target is None: target = [None]*len(source)
545 for tgt, src in zip(target, source):
546 if not tgt is None: tgt = [tgt]
547 if not src is None: src = [src]
548 result.extend(self._execute(env, tgt, src, overwarn))
549 return SCons.Node.NodeList(result)
550
551 overwarn.warn()
552
553 tlist, slist = self._create_nodes(env, target, source)
554
555 # Check for errors with the specified target/source lists.
556 _node_errors(self, env, tlist, slist)
557
558 # The targets are fine, so find or make the appropriate Executor to
559 # build this particular list of targets from this particular list of
560 # sources.
561
562 executor = None
563 key = None
564
565 if self.multi:
566 try:
567 executor = tlist[0].get_executor(create = 0)
568 except (AttributeError, IndexError):
569 pass
570 else:
571 executor.add_sources(slist)
572
573 if executor is None:
574 if not self.action:
575 fmt = "Builder %s must have an action to build %s."
576 raise UserError(fmt % (self.get_name(env or self.env),
577 list(map(str,tlist))))
578 key = self.action.batch_key(env or self.env, tlist, slist)
579 if key:
580 try:
581 executor = SCons.Executor.GetBatchExecutor(key)
582 except KeyError:
583 pass
584 else:
585 executor.add_batch(tlist, slist)
586
587 if executor is None:
588 executor = SCons.Executor.Executor(self.action, env, [],
589 tlist, slist, executor_kw)
590 if key:
591 SCons.Executor.AddBatchExecutor(key, executor)
592
593 # Now set up the relevant information in the target Nodes themselves.
594 for t in tlist:
595 t.cwd = env.fs.getcwd()
596 t.builder_set(self)
597 t.env_set(env)
598 t.add_source(slist)
599 t.set_executor(executor)
600 t.set_explicit(self.is_explicit)
601
602 return SCons.Node.NodeList(tlist)
603
604 def __call__(self, env, target=None, source=None, chdir=_null, **kw):
605 # We now assume that target and source are lists or None.
606 # The caller (typically Environment.BuilderWrapper) is
607 # responsible for converting any scalar values to lists.
608 if chdir is _null:
609 ekw = self.executor_kw
610 else:
611 ekw = self.executor_kw.copy()
612 ekw['chdir'] = chdir
613 if kw:
614 if 'srcdir' in kw:
615 def prependDirIfRelative(f, srcdir=kw['srcdir']):
616 import os.path
617 if SCons.Util.is_String(f) and not os.path.isabs(f):
618 f = os.path.join(srcdir, f)
619 return f
620 if not SCons.Util.is_List(source):
621 source = [source]
622 source = list(map(prependDirIfRelative, source))
623 del kw['srcdir']
624 if self.overrides:
625 env_kw = self.overrides.copy()
626 env_kw.update(kw)
627 else:
628 env_kw = kw
629 else:
630 env_kw = self.overrides
631 env = env.Override(env_kw)
632 return self._execute(env, target, source, OverrideWarner(kw), ekw)
633
634 def adjust_suffix(self, suff):
635 if suff and not suff[0] in [ '.', '_', '$' ]:
636 return '.' + suff
637 return suff
638
639 def get_prefix(self, env, sources=[]):
640 prefix = self.prefix
641 if callable(prefix):
642 prefix = prefix(env, sources)
643 return env.subst(prefix)
644
645 def set_suffix(self, suffix):
646 if not callable(suffix):
647 suffix = self.adjust_suffix(suffix)
648 self.suffix = suffix
649
650 def get_suffix(self, env, sources=[]):
651 suffix = self.suffix
652 if callable(suffix):
653 suffix = suffix(env, sources)
654 return env.subst(suffix)
655
656 def set_src_suffix(self, src_suffix):
657 if not src_suffix:
658 src_suffix = []
659 elif not SCons.Util.is_List(src_suffix):
660 src_suffix = [ src_suffix ]
661 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
662
663 def get_src_suffix(self, env):
664 """Get the first src_suffix in the list of src_suffixes."""
665 ret = self.src_suffixes(env)
666 if not ret:
667 return ''
668 return ret[0]
669
670 def add_emitter(self, suffix, emitter):
671 """Add a suffix-emitter mapping to this Builder.
672
673 This assumes that emitter has been initialized with an
674 appropriate dictionary type, and will throw a TypeError if
675 not, so the caller is responsible for knowing that this is an
676 appropriate method to call for the Builder in question.
677 """
678 self.emitter[suffix] = emitter
679
680 def add_src_builder(self, builder):
681 """
682 Add a new Builder to the list of src_builders.
683
684 This requires wiping out cached values so that the computed
685 lists of source suffixes get re-calculated.
686 """
687 self._memo = {}
688 self.src_builder.append(builder)
689
690 def _get_sdict(self, env):
691 """
692 Returns a dictionary mapping all of the source suffixes of all
693 src_builders of this Builder to the underlying Builder that
694 should be called first.
695
696 This dictionary is used for each target specified, so we save a
697 lot of extra computation by memoizing it for each construction
698 environment.
699
700 Note that this is re-computed each time, not cached, because there
701 might be changes to one of our source Builders (or one of their
702 source Builders, and so on, and so on...) that we can't "see."
703
704 The underlying methods we call cache their computed values,
705 though, so we hope repeatedly aggregating them into a dictionary
706 like this won't be too big a hit. We may need to look for a
707 better way to do this if performance data show this has turned
708 into a significant bottleneck.
709 """
710 sdict = {}
711 for bld in self.get_src_builders(env):
712 for suf in bld.src_suffixes(env):
713 sdict[suf] = bld
714 return sdict
715
716 def src_builder_sources(self, env, source, overwarn={}):
717 sdict = self._get_sdict(env)
718
719 src_suffixes = self.src_suffixes(env)
720
721 lengths = list(set(map(len, src_suffixes)))
722
723 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
724 node_suffixes = [name[-l:] for l in lengths]
725 for suf in src_suffixes:
726 if suf in node_suffixes:
727 return suf
728 return None
729
730 result = []
731 for s in SCons.Util.flatten(source):
732 if SCons.Util.is_String(s):
733 match_suffix = match_src_suffix(env.subst(s))
734 if not match_suffix and not '.' in s:
735 src_suf = self.get_src_suffix(env)
736 s = self._adjustixes(s, None, src_suf)[0]
737 else:
738 match_suffix = match_src_suffix(s.name)
739 if match_suffix:
740 try:
741 bld = sdict[match_suffix]
742 except KeyError:
743 result.append(s)
744 else:
745 tlist = bld._execute(env, None, [s], overwarn)
746 # If the subsidiary Builder returned more than one
747 # target, then filter out any sources that this
748 # Builder isn't capable of building.
749 if len(tlist) > 1:
750 tlist = [t for t in tlist if match_src_suffix(t.name)]
751 result.extend(tlist)
752 else:
753 result.append(s)
754
755 source_factory = env.get_factory(self.source_factory)
756
757 return env.arg2nodes(result, source_factory)
758
759 def _get_src_builders_key(self, env):
760 return id(env)
761
762 memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_sr c_builders_key))
763
764 def get_src_builders(self, env):
765 """
766 Returns the list of source Builders for this Builder.
767
768 This exists mainly to look up Builders referenced as
769 strings in the 'BUILDER' variable of the construction
770 environment and cache the result.
771 """
772 memo_key = id(env)
773 try:
774 memo_dict = self._memo['get_src_builders']
775 except KeyError:
776 memo_dict = {}
777 self._memo['get_src_builders'] = memo_dict
778 else:
779 try:
780 return memo_dict[memo_key]
781 except KeyError:
782 pass
783
784 builders = []
785 for bld in self.src_builder:
786 if SCons.Util.is_String(bld):
787 try:
788 bld = env['BUILDERS'][bld]
789 except KeyError:
790 continue
791 builders.append(bld)
792
793 memo_dict[memo_key] = builders
794 return builders
795
796 def _subst_src_suffixes_key(self, env):
797 return id(env)
798
799 memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subs t_src_suffixes_key))
800
801 def subst_src_suffixes(self, env):
802 """
803 The suffix list may contain construction variable expansions,
804 so we have to evaluate the individual strings. To avoid doing
805 this over and over, we memoize the results for each construction
806 environment.
807 """
808 memo_key = id(env)
809 try:
810 memo_dict = self._memo['subst_src_suffixes']
811 except KeyError:
812 memo_dict = {}
813 self._memo['subst_src_suffixes'] = memo_dict
814 else:
815 try:
816 return memo_dict[memo_key]
817 except KeyError:
818 pass
819 suffixes = [env.subst(x) for x in self.src_suffix]
820 memo_dict[memo_key] = suffixes
821 return suffixes
822
823 def src_suffixes(self, env):
824 """
825 Returns the list of source suffixes for all src_builders of this
826 Builder.
827
828 This is essentially a recursive descent of the src_builder "tree."
829 (This value isn't cached because there may be changes in a
830 src_builder many levels deep that we can't see.)
831 """
832 sdict = {}
833 suffixes = self.subst_src_suffixes(env)
834 for s in suffixes:
835 sdict[s] = 1
836 for builder in self.get_src_builders(env):
837 for s in builder.src_suffixes(env):
838 if s not in sdict:
839 sdict[s] = 1
840 suffixes.append(s)
841 return suffixes
842
843 class CompositeBuilder(SCons.Util.Proxy):
844 """A Builder Proxy whose main purpose is to always have
845 a DictCmdGenerator as its action, and to provide access
846 to the DictCmdGenerator's add_action() method.
847 """
848
849 def __init__(self, builder, cmdgen):
850 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
851 SCons.Util.Proxy.__init__(self, builder)
852
853 # cmdgen should always be an instance of DictCmdGenerator.
854 self.cmdgen = cmdgen
855 self.builder = builder
856
857 __call__ = SCons.Util.Delegate('__call__')
858
859 def add_action(self, suffix, action):
860 self.cmdgen.add_action(suffix, action)
861 self.set_src_suffix(self.cmdgen.src_suffixes())
862
863 def is_a_Builder(obj):
864 """"Returns True iff the specified obj is one of our Builder classes.
865
866 The test is complicated a bit by the fact that CompositeBuilder
867 is a proxy, not a subclass of BuilderBase.
868 """
869 return (isinstance(obj, BuilderBase)
870 or isinstance(obj, CompositeBuilder)
871 or callable(obj))
872
873 # Local Variables:
874 # tab-width:4
875 # indent-tabs-mode:nil
876 # End:
877 # vim: set expandtab tabstop=4 shiftwidth=4:
OLDNEW
« no previous file with comments | « scons-2.0.1/engine/SCons/Action.py ('k') | scons-2.0.1/engine/SCons/CacheDir.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698