Package SCons :: Module Builder
[hide private]
[frames] | no frames]

Source Code for Module SCons.Builder

  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 - 2015 The SCons 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 rel_2.3.5:3347:d31d5a4e74b6 2015/07/31 14:36:10 bdbaddog" 
101   
102  import collections 
103   
104  import SCons.Action 
105  import SCons.Debug 
106  from SCons.Debug import logInstanceCreation 
107  from SCons.Errors import InternalError, UserError 
108  import SCons.Executor 
109  import SCons.Memoize 
110  import SCons.Util 
111  import SCons.Warnings 
112   
113 -class _Null(object):
114 pass
115 116 _null = _Null 117
118 -def match_splitext(path, suffixes = []):
119 if suffixes: 120 matchsuf = [S for S in suffixes if path[-len(S):] == S] 121 if matchsuf: 122 suf = max([(len(_f),_f) for _f in matchsuf])[1] 123 return [path[:-len(suf)], path[-len(suf):]] 124 return SCons.Util.splitext(path)
125
126 -class DictCmdGenerator(SCons.Util.Selector):
127 """This is a callable class that can be used as a 128 command generator function. It holds on to a dictionary 129 mapping file suffixes to Actions. It uses that dictionary 130 to return the proper action based on the file suffix of 131 the source file.""" 132
133 - def __init__(self, dict=None, source_ext_match=1):
134 SCons.Util.Selector.__init__(self, dict) 135 self.source_ext_match = source_ext_match
136
137 - def src_suffixes(self):
138 return list(self.keys())
139
140 - def add_action(self, suffix, action):
141 """Add a suffix-action pair to the mapping. 142 """ 143 self[suffix] = action
144
145 - def __call__(self, target, source, env, for_signature):
146 if not source: 147 return [] 148 149 if self.source_ext_match: 150 suffixes = self.src_suffixes() 151 ext = None 152 for src in map(str, source): 153 my_ext = match_splitext(src, suffixes)[1] 154 if ext and my_ext != ext: 155 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" 156 % (repr(list(map(str, target))), src, ext, my_ext)) 157 ext = my_ext 158 else: 159 ext = match_splitext(str(source[0]), self.src_suffixes())[1] 160 161 if not ext: 162 #return ext 163 raise UserError("While building `%s': " 164 "Cannot deduce file extension from source files: %s" 165 % (repr(list(map(str, target))), repr(list(map(str, source))))) 166 167 try: 168 ret = SCons.Util.Selector.__call__(self, env, source, ext) 169 except KeyError, e: 170 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) 171 if ret is None: 172 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." % \ 173 (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) 174 return ret
175
176 -class CallableSelector(SCons.Util.Selector):
177 """A callable dictionary that will, in turn, call the value it 178 finds if it can."""
179 - def __call__(self, env, source):
180 value = SCons.Util.Selector.__call__(self, env, source) 181 if callable(value): 182 value = value(env, source) 183 return value
184
185 -class DictEmitter(SCons.Util.Selector):
186 """A callable dictionary that maps file suffixes to emitters. 187 When called, it finds the right emitter in its dictionary for the 188 suffix of the first source file, and calls that emitter to get the 189 right lists of targets and sources to return. If there's no emitter 190 for the suffix in its dictionary, the original target and source are 191 returned. 192 """
193 - def __call__(self, target, source, env):
194 emitter = SCons.Util.Selector.__call__(self, env, source) 195 if emitter: 196 target, source = emitter(target, source, env) 197 return (target, source)
198
199 -class ListEmitter(collections.UserList):
200 """A callable list of emitters that calls each in sequence, 201 returning the result. 202 """
203 - def __call__(self, target, source, env):
204 for e in self.data: 205 target, source = e(target, source, env) 206 return (target, source)
207 208 # These are a common errors when calling a Builder; 209 # they are similar to the 'target' and 'source' keyword args to builders, 210 # so we issue warnings when we see them. The warnings can, of course, 211 # be disabled. 212 misleading_keywords = { 213 'targets' : 'target', 214 'sources' : 'source', 215 } 216
217 -class OverrideWarner(collections.UserDict):
218 """A class for warning about keyword arguments that we use as 219 overrides in a Builder call. 220 221 This class exists to handle the fact that a single Builder call 222 can actually invoke multiple builders. This class only emits the 223 warnings once, no matter how many Builders are invoked. 224 """
225 - def __init__(self, dict):
226 collections.UserDict.__init__(self, dict) 227 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner') 228 self.already_warned = None
229 - def warn(self):
230 if self.already_warned: 231 return 232 for k in self.keys(): 233 if k in misleading_keywords: 234 alt = misleading_keywords[k] 235 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) 236 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) 237 self.already_warned = 1
238
239 -def Builder(**kw):
240 """A factory for builder objects.""" 241 composite = None 242 if 'generator' in kw: 243 if 'action' in kw: 244 raise UserError("You must not specify both an action and a generator.") 245 kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) 246 del kw['generator'] 247 elif 'action' in kw: 248 source_ext_match = kw.get('source_ext_match', 1) 249 if 'source_ext_match' in kw: 250 del kw['source_ext_match'] 251 if SCons.Util.is_Dict(kw['action']): 252 composite = DictCmdGenerator(kw['action'], source_ext_match) 253 kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) 254 kw['src_suffix'] = composite.src_suffixes() 255 else: 256 kw['action'] = SCons.Action.Action(kw['action']) 257 258 if 'emitter' in kw: 259 emitter = kw['emitter'] 260 if SCons.Util.is_String(emitter): 261 # This allows users to pass in an Environment 262 # variable reference (like "$FOO") as an emitter. 263 # We will look in that Environment variable for 264 # a callable to use as the actual emitter. 265 var = SCons.Util.get_environment_var(emitter) 266 if not var: 267 raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) 268 kw['emitter'] = EmitterProxy(var) 269 elif SCons.Util.is_Dict(emitter): 270 kw['emitter'] = DictEmitter(emitter) 271 elif SCons.Util.is_List(emitter): 272 kw['emitter'] = ListEmitter(emitter) 273 274 result = BuilderBase(**kw) 275 276 if not composite is None: 277 result = CompositeBuilder(result, composite) 278 279 return result
280
281 -def _node_errors(builder, env, tlist, slist):
282 """Validate that the lists of target and source nodes are 283 legal for this builder and environment. Raise errors or 284 issue warnings as appropriate. 285 """ 286 287 # First, figure out if there are any errors in the way the targets 288 # were specified. 289 for t in tlist: 290 if t.side_effect: 291 raise UserError("Multiple ways to build the same target were specified for: %s" % t) 292 if t.has_explicit_builder(): 293 if not t.env is None and not t.env is env: 294 action = t.builder.action 295 t_contents = action.get_contents(tlist, slist, t.env) 296 contents = action.get_contents(tlist, slist, env) 297 298 if t_contents == contents: 299 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)) 300 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) 301 else: 302 msg = "Two environments with different actions were specified for the same target: %s" % t 303 raise UserError(msg) 304 if builder.multi: 305 if t.builder != builder: 306 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) 307 raise UserError(msg) 308 # TODO(batch): list constructed each time! 309 if t.get_executor().get_all_targets() != tlist: 310 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))) 311 raise UserError(msg) 312 elif t.sources != slist: 313 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))) 314 raise UserError(msg) 315 316 if builder.single_source: 317 if len(slist) > 1: 318 raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
319
320 -class EmitterProxy(object):
321 """This is a callable class that can act as a 322 Builder emitter. It holds on to a string that 323 is a key into an Environment dictionary, and will 324 look there at actual build time to see if it holds 325 a callable. If so, we will call that as the actual 326 emitter."""
327 - def __init__(self, var):
328 self.var = SCons.Util.to_String(var)
329
330 - def __call__(self, target, source, env):
331 emitter = self.var 332 333 # Recursively substitute the variable. 334 # We can't use env.subst() because it deals only 335 # in strings. Maybe we should change that? 336 while SCons.Util.is_String(emitter) and emitter in env: 337 emitter = env[emitter] 338 if callable(emitter): 339 target, source = emitter(target, source, env) 340 elif SCons.Util.is_List(emitter): 341 for e in emitter: 342 target, source = e(target, source, env) 343 344 return (target, source)
345 346
347 - def __cmp__(self, other):
348 return cmp(self.var, other.var)
349
350 -class BuilderBase(object):
351 """Base class for Builders, objects that create output 352 nodes (files) from input nodes (files). 353 """ 354 355 if SCons.Memoize.use_memoizer: 356 __metaclass__ = SCons.Memoize.Memoized_Metaclass 357 358 memoizer_counters = [] 359
360 - def __init__(self, action = None, 361 prefix = '', 362 suffix = '', 363 src_suffix = '', 364 target_factory = None, 365 source_factory = None, 366 target_scanner = None, 367 source_scanner = None, 368 emitter = None, 369 multi = 0, 370 env = None, 371 single_source = 0, 372 name = None, 373 chdir = _null, 374 is_explicit = 1, 375 src_builder = None, 376 ensure_suffix = False, 377 **overrides):
378 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase') 379 self._memo = {} 380 self.action = action 381 self.multi = multi 382 if SCons.Util.is_Dict(prefix): 383 prefix = CallableSelector(prefix) 384 self.prefix = prefix 385 if SCons.Util.is_Dict(suffix): 386 suffix = CallableSelector(suffix) 387 self.env = env 388 self.single_source = single_source 389 if 'overrides' in overrides: 390 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 391 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ 392 "\tspecify the items as keyword arguments to the Builder() call instead.") 393 overrides.update(overrides['overrides']) 394 del overrides['overrides'] 395 if 'scanner' in overrides: 396 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 397 "The \"scanner\" keyword to Builder() creation has been deprecated;\n" 398 "\tuse: source_scanner or target_scanner as appropriate.") 399 del overrides['scanner'] 400 self.overrides = overrides 401 402 self.set_suffix(suffix) 403 self.set_src_suffix(src_suffix) 404 self.ensure_suffix = ensure_suffix 405 406 self.target_factory = target_factory 407 self.source_factory = source_factory 408 self.target_scanner = target_scanner 409 self.source_scanner = source_scanner 410 411 self.emitter = emitter 412 413 # Optional Builder name should only be used for Builders 414 # that don't get attached to construction environments. 415 if name: 416 self.name = name 417 self.executor_kw = {} 418 if not chdir is _null: 419 self.executor_kw['chdir'] = chdir 420 self.is_explicit = is_explicit 421 422 if src_builder is None: 423 src_builder = [] 424 elif not SCons.Util.is_List(src_builder): 425 src_builder = [ src_builder ] 426 self.src_builder = src_builder
427
428 - def __nonzero__(self):
429 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
430
431 - def get_name(self, env):
432 """Attempts to get the name of the Builder. 433 434 Look at the BUILDERS variable of env, expecting it to be a 435 dictionary containing this Builder, and return the key of the 436 dictionary. If there's no key, then return a directly-configured 437 name (if there is one) or the name of the class (by default).""" 438 439 try: 440 index = list(env['BUILDERS'].values()).index(self) 441 return list(env['BUILDERS'].keys())[index] 442 except (AttributeError, KeyError, TypeError, ValueError): 443 try: 444 return self.name 445 except AttributeError: 446 return str(self.__class__)
447
448 - def __cmp__(self, other):
449 return cmp(self.__dict__, other.__dict__)
450
451 - def splitext(self, path, env=None):
452 if not env: 453 env = self.env 454 if env: 455 suffixes = self.src_suffixes(env) 456 else: 457 suffixes = [] 458 return match_splitext(path, suffixes)
459
460 - def _adjustixes(self, files, pre, suf, ensure_suffix=False):
461 if not files: 462 return [] 463 result = [] 464 if not SCons.Util.is_List(files): 465 files = [files] 466 467 for f in files: 468 if SCons.Util.is_String(f): 469 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) 470 result.append(f) 471 return result
472
473 - def _create_nodes(self, env, target = None, source = None):
474 """Create and return lists of target and source nodes. 475 """ 476 src_suf = self.get_src_suffix(env) 477 478 target_factory = env.get_factory(self.target_factory) 479 source_factory = env.get_factory(self.source_factory) 480 481 source = self._adjustixes(source, None, src_suf) 482 slist = env.arg2nodes(source, source_factory) 483 484 pre = self.get_prefix(env, slist) 485 suf = self.get_suffix(env, slist) 486 487 if target is None: 488 try: 489 t_from_s = slist[0].target_from_source 490 except AttributeError: 491 raise UserError("Do not know how to create a target from source `%s'" % slist[0]) 492 except IndexError: 493 tlist = [] 494 else: 495 splitext = lambda S: self.splitext(S,env) 496 tlist = [ t_from_s(pre, suf, splitext) ] 497 else: 498 target = self._adjustixes(target, pre, suf, self.ensure_suffix) 499 tlist = env.arg2nodes(target, target_factory, target=target, source=source) 500 501 if self.emitter: 502 # The emitter is going to do str(node), but because we're 503 # being called *from* a builder invocation, the new targets 504 # don't yet have a builder set on them and will look like 505 # source files. Fool the emitter's str() calls by setting 506 # up a temporary builder on the new targets. 507 new_targets = [] 508 for t in tlist: 509 if not t.is_derived(): 510 t.builder_set(self) 511 new_targets.append(t) 512 513 orig_tlist = tlist[:] 514 orig_slist = slist[:] 515 516 target, source = self.emitter(target=tlist, source=slist, env=env) 517 518 # Now delete the temporary builders that we attached to any 519 # new targets, so that _node_errors() doesn't do weird stuff 520 # to them because it thinks they already have builders. 521 for t in new_targets: 522 if t.builder is self: 523 # Only delete the temporary builder if the emitter 524 # didn't change it on us. 525 t.builder_set(None) 526 527 # Have to call arg2nodes yet again, since it is legal for 528 # emitters to spit out strings as well as Node instances. 529 tlist = env.arg2nodes(target, target_factory, 530 target=orig_tlist, source=orig_slist) 531 slist = env.arg2nodes(source, source_factory, 532 target=orig_tlist, source=orig_slist) 533 534 return tlist, slist
535
536 - def _execute(self, env, target, source, overwarn={}, executor_kw={}):
537 # We now assume that target and source are lists or None. 538 if self.src_builder: 539 source = self.src_builder_sources(env, source, overwarn) 540 541 if self.single_source and len(source) > 1 and target is None: 542 result = [] 543 if target is None: target = [None]*len(source) 544 for tgt, src in zip(target, source): 545 if not tgt is None: tgt = [tgt] 546 if not src is None: src = [src] 547 result.extend(self._execute(env, tgt, src, overwarn)) 548 return SCons.Node.NodeList(result) 549 550 overwarn.warn() 551 552 tlist, slist = self._create_nodes(env, target, source) 553 554 # Check for errors with the specified target/source lists. 555 _node_errors(self, env, tlist, slist) 556 557 # The targets are fine, so find or make the appropriate Executor to 558 # build this particular list of targets from this particular list of 559 # sources. 560 561 executor = None 562 key = None 563 564 if self.multi: 565 try: 566 executor = tlist[0].get_executor(create = 0) 567 except (AttributeError, IndexError): 568 pass 569 else: 570 executor.add_sources(slist) 571 572 if executor is None: 573 if not self.action: 574 fmt = "Builder %s must have an action to build %s." 575 raise UserError(fmt % (self.get_name(env or self.env), 576 list(map(str,tlist)))) 577 key = self.action.batch_key(env or self.env, tlist, slist) 578 if key: 579 try: 580 executor = SCons.Executor.GetBatchExecutor(key) 581 except KeyError: 582 pass 583 else: 584 executor.add_batch(tlist, slist) 585 586 if executor is None: 587 executor = SCons.Executor.Executor(self.action, env, [], 588 tlist, slist, executor_kw) 589 if key: 590 SCons.Executor.AddBatchExecutor(key, executor) 591 592 # Now set up the relevant information in the target Nodes themselves. 593 for t in tlist: 594 t.cwd = env.fs.getcwd() 595 t.builder_set(self) 596 t.env_set(env) 597 t.add_source(slist) 598 t.set_executor(executor) 599 t.set_explicit(self.is_explicit) 600 601 return SCons.Node.NodeList(tlist)
602
603 - def __call__(self, env, target=None, source=None, chdir=_null, **kw):
604 # We now assume that target and source are lists or None. 605 # The caller (typically Environment.BuilderWrapper) is 606 # responsible for converting any scalar values to lists. 607 if chdir is _null: 608 ekw = self.executor_kw 609 else: 610 ekw = self.executor_kw.copy() 611 ekw['chdir'] = chdir 612 if kw: 613 if 'srcdir' in kw: 614 def prependDirIfRelative(f, srcdir=kw['srcdir']): 615 import os.path 616 if SCons.Util.is_String(f) and not os.path.isabs(f): 617 f = os.path.join(srcdir, f) 618 return f
619 if not SCons.Util.is_List(source): 620 source = [source] 621 source = list(map(prependDirIfRelative, source)) 622 del kw['srcdir'] 623 if self.overrides: 624 env_kw = self.overrides.copy() 625 env_kw.update(kw) 626 else: 627 env_kw = kw 628 else: 629 env_kw = self.overrides 630 env = env.Override(env_kw) 631 return self._execute(env, target, source, OverrideWarner(kw), ekw)
632
633 - def adjust_suffix(self, suff):
634 if suff and not suff[0] in [ '.', '_', '$' ]: 635 return '.' + suff 636 return suff
637
638 - def get_prefix(self, env, sources=[]):
639 prefix = self.prefix 640 if callable(prefix): 641 prefix = prefix(env, sources) 642 return env.subst(prefix)
643
644 - def set_suffix(self, suffix):
645 if not callable(suffix): 646 suffix = self.adjust_suffix(suffix) 647 self.suffix = suffix
648
649 - def get_suffix(self, env, sources=[]):
650 suffix = self.suffix 651 if callable(suffix): 652 suffix = suffix(env, sources) 653 return env.subst(suffix)
654
655 - def set_src_suffix(self, src_suffix):
656 if not src_suffix: 657 src_suffix = [] 658 elif not SCons.Util.is_List(src_suffix): 659 src_suffix = [ src_suffix ] 660 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
661
662 - def get_src_suffix(self, env):
663 """Get the first src_suffix in the list of src_suffixes.""" 664 ret = self.src_suffixes(env) 665 if not ret: 666 return '' 667 return ret[0]
668
669 - def add_emitter(self, suffix, emitter):
670 """Add a suffix-emitter mapping to this Builder. 671 672 This assumes that emitter has been initialized with an 673 appropriate dictionary type, and will throw a TypeError if 674 not, so the caller is responsible for knowing that this is an 675 appropriate method to call for the Builder in question. 676 """ 677 self.emitter[suffix] = emitter
678
679 - def add_src_builder(self, builder):
680 """ 681 Add a new Builder to the list of src_builders. 682 683 This requires wiping out cached values so that the computed 684 lists of source suffixes get re-calculated. 685 """ 686 self._memo = {} 687 self.src_builder.append(builder)
688
689 - def _get_sdict(self, env):
690 """ 691 Returns a dictionary mapping all of the source suffixes of all 692 src_builders of this Builder to the underlying Builder that 693 should be called first. 694 695 This dictionary is used for each target specified, so we save a 696 lot of extra computation by memoizing it for each construction 697 environment. 698 699 Note that this is re-computed each time, not cached, because there 700 might be changes to one of our source Builders (or one of their 701 source Builders, and so on, and so on...) that we can't "see." 702 703 The underlying methods we call cache their computed values, 704 though, so we hope repeatedly aggregating them into a dictionary 705 like this won't be too big a hit. We may need to look for a 706 better way to do this if performance data show this has turned 707 into a significant bottleneck. 708 """ 709 sdict = {} 710 for bld in self.get_src_builders(env): 711 for suf in bld.src_suffixes(env): 712 sdict[suf] = bld 713 return sdict
714
715 - def src_builder_sources(self, env, source, overwarn={}):
716 sdict = self._get_sdict(env) 717 718 src_suffixes = self.src_suffixes(env) 719 720 lengths = list(set(map(len, src_suffixes))) 721 722 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): 723 node_suffixes = [name[-l:] for l in lengths] 724 for suf in src_suffixes: 725 if suf in node_suffixes: 726 return suf 727 return None
728 729 result = [] 730 for s in SCons.Util.flatten(source): 731 if SCons.Util.is_String(s): 732 match_suffix = match_src_suffix(env.subst(s)) 733 if not match_suffix and not '.' in s: 734 src_suf = self.get_src_suffix(env) 735 s = self._adjustixes(s, None, src_suf)[0] 736 else: 737 match_suffix = match_src_suffix(s.name) 738 if match_suffix: 739 try: 740 bld = sdict[match_suffix] 741 except KeyError: 742 result.append(s) 743 else: 744 tlist = bld._execute(env, None, [s], overwarn) 745 # If the subsidiary Builder returned more than one 746 # target, then filter out any sources that this 747 # Builder isn't capable of building. 748 if len(tlist) > 1: 749 tlist = [t for t in tlist if match_src_suffix(t.name)] 750 result.extend(tlist) 751 else: 752 result.append(s) 753 754 source_factory = env.get_factory(self.source_factory) 755 756 return env.arg2nodes(result, source_factory) 757
758 - def _get_src_builders_key(self, env):
759 return id(env)
760 761 memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) 762
763 - def get_src_builders(self, env):
764 """ 765 Returns the list of source Builders for this Builder. 766 767 This exists mainly to look up Builders referenced as 768 strings in the 'BUILDER' variable of the construction 769 environment and cache the result. 770 """ 771 memo_key = id(env) 772 try: 773 memo_dict = self._memo['get_src_builders'] 774 except KeyError: 775 memo_dict = {} 776 self._memo['get_src_builders'] = memo_dict 777 else: 778 try: 779 return memo_dict[memo_key] 780 except KeyError: 781 pass 782 783 builders = [] 784 for bld in self.src_builder: 785 if SCons.Util.is_String(bld): 786 try: 787 bld = env['BUILDERS'][bld] 788 except KeyError: 789 continue 790 builders.append(bld) 791 792 memo_dict[memo_key] = builders 793 return builders
794
795 - def _subst_src_suffixes_key(self, env):
796 return id(env)
797 798 memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) 799
800 - def subst_src_suffixes(self, env):
801 """ 802 The suffix list may contain construction variable expansions, 803 so we have to evaluate the individual strings. To avoid doing 804 this over and over, we memoize the results for each construction 805 environment. 806 """ 807 memo_key = id(env) 808 try: 809 memo_dict = self._memo['subst_src_suffixes'] 810 except KeyError: 811 memo_dict = {} 812 self._memo['subst_src_suffixes'] = memo_dict 813 else: 814 try: 815 return memo_dict[memo_key] 816 except KeyError: 817 pass 818 suffixes = [env.subst(x) for x in self.src_suffix] 819 memo_dict[memo_key] = suffixes 820 return suffixes
821
822 - def src_suffixes(self, env):
823 """ 824 Returns the list of source suffixes for all src_builders of this 825 Builder. 826 827 This is essentially a recursive descent of the src_builder "tree." 828 (This value isn't cached because there may be changes in a 829 src_builder many levels deep that we can't see.) 830 """ 831 sdict = {} 832 suffixes = self.subst_src_suffixes(env) 833 for s in suffixes: 834 sdict[s] = 1 835 for builder in self.get_src_builders(env): 836 for s in builder.src_suffixes(env): 837 if s not in sdict: 838 sdict[s] = 1 839 suffixes.append(s) 840 return suffixes
841
842 -class CompositeBuilder(SCons.Util.Proxy):
843 """A Builder Proxy whose main purpose is to always have 844 a DictCmdGenerator as its action, and to provide access 845 to the DictCmdGenerator's add_action() method. 846 """ 847
848 - def __init__(self, builder, cmdgen):
849 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.CompositeBuilder') 850 SCons.Util.Proxy.__init__(self, builder) 851 852 # cmdgen should always be an instance of DictCmdGenerator. 853 self.cmdgen = cmdgen 854 self.builder = builder
855 856 __call__ = SCons.Util.Delegate('__call__') 857
858 - def add_action(self, suffix, action):
859 self.cmdgen.add_action(suffix, action) 860 self.set_src_suffix(self.cmdgen.src_suffixes())
861
862 -def is_a_Builder(obj):
863 """"Returns True if the specified obj is one of our Builder classes. 864 865 The test is complicated a bit by the fact that CompositeBuilder 866 is a proxy, not a subclass of BuilderBase. 867 """ 868 return (isinstance(obj, BuilderBase) 869 or isinstance(obj, CompositeBuilder) 870 or callable(obj))
871 872 # Local Variables: 873 # tab-width:4 874 # indent-tabs-mode:nil 875 # End: 876 # vim: set expandtab tabstop=4 shiftwidth=4: 877