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.4.1:3453:73fefd3ea0b0 2015/11/09 03:25:05 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 = t.builder.action.get_contents(tlist, slist, t.env) 296 contents = builder.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 - def __init__(self, action = None, 356 prefix = '', 357 suffix = '', 358 src_suffix = '', 359 target_factory = None, 360 source_factory = None, 361 target_scanner = None, 362 source_scanner = None, 363 emitter = None, 364 multi = 0, 365 env = None, 366 single_source = 0, 367 name = None, 368 chdir = _null, 369 is_explicit = 1, 370 src_builder = None, 371 ensure_suffix = False, 372 **overrides):
373 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase') 374 self._memo = {} 375 self.action = action 376 self.multi = multi 377 if SCons.Util.is_Dict(prefix): 378 prefix = CallableSelector(prefix) 379 self.prefix = prefix 380 if SCons.Util.is_Dict(suffix): 381 suffix = CallableSelector(suffix) 382 self.env = env 383 self.single_source = single_source 384 if 'overrides' in overrides: 385 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 386 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ 387 "\tspecify the items as keyword arguments to the Builder() call instead.") 388 overrides.update(overrides['overrides']) 389 del overrides['overrides'] 390 if 'scanner' in overrides: 391 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 392 "The \"scanner\" keyword to Builder() creation has been deprecated;\n" 393 "\tuse: source_scanner or target_scanner as appropriate.") 394 del overrides['scanner'] 395 self.overrides = overrides 396 397 self.set_suffix(suffix) 398 self.set_src_suffix(src_suffix) 399 self.ensure_suffix = ensure_suffix 400 401 self.target_factory = target_factory 402 self.source_factory = source_factory 403 self.target_scanner = target_scanner 404 self.source_scanner = source_scanner 405 406 self.emitter = emitter 407 408 # Optional Builder name should only be used for Builders 409 # that don't get attached to construction environments. 410 if name: 411 self.name = name 412 self.executor_kw = {} 413 if not chdir is _null: 414 self.executor_kw['chdir'] = chdir 415 self.is_explicit = is_explicit 416 417 if src_builder is None: 418 src_builder = [] 419 elif not SCons.Util.is_List(src_builder): 420 src_builder = [ src_builder ] 421 self.src_builder = src_builder
422
423 - def __nonzero__(self):
424 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
425
426 - def get_name(self, env):
427 """Attempts to get the name of the Builder. 428 429 Look at the BUILDERS variable of env, expecting it to be a 430 dictionary containing this Builder, and return the key of the 431 dictionary. If there's no key, then return a directly-configured 432 name (if there is one) or the name of the class (by default).""" 433 434 try: 435 index = list(env['BUILDERS'].values()).index(self) 436 return list(env['BUILDERS'].keys())[index] 437 except (AttributeError, KeyError, TypeError, ValueError): 438 try: 439 return self.name 440 except AttributeError: 441 return str(self.__class__)
442
443 - def __cmp__(self, other):
444 return cmp(self.__dict__, other.__dict__)
445
446 - def splitext(self, path, env=None):
447 if not env: 448 env = self.env 449 if env: 450 suffixes = self.src_suffixes(env) 451 else: 452 suffixes = [] 453 return match_splitext(path, suffixes)
454
455 - def _adjustixes(self, files, pre, suf, ensure_suffix=False):
456 if not files: 457 return [] 458 result = [] 459 if not SCons.Util.is_List(files): 460 files = [files] 461 462 for f in files: 463 if SCons.Util.is_String(f): 464 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) 465 result.append(f) 466 return result
467
468 - def _create_nodes(self, env, target = None, source = None):
469 """Create and return lists of target and source nodes. 470 """ 471 src_suf = self.get_src_suffix(env) 472 473 target_factory = env.get_factory(self.target_factory) 474 source_factory = env.get_factory(self.source_factory) 475 476 source = self._adjustixes(source, None, src_suf) 477 slist = env.arg2nodes(source, source_factory) 478 479 pre = self.get_prefix(env, slist) 480 suf = self.get_suffix(env, slist) 481 482 if target is None: 483 try: 484 t_from_s = slist[0].target_from_source 485 except AttributeError: 486 raise UserError("Do not know how to create a target from source `%s'" % slist[0]) 487 except IndexError: 488 tlist = [] 489 else: 490 splitext = lambda S: self.splitext(S,env) 491 tlist = [ t_from_s(pre, suf, splitext) ] 492 else: 493 target = self._adjustixes(target, pre, suf, self.ensure_suffix) 494 tlist = env.arg2nodes(target, target_factory, target=target, source=source) 495 496 if self.emitter: 497 # The emitter is going to do str(node), but because we're 498 # being called *from* a builder invocation, the new targets 499 # don't yet have a builder set on them and will look like 500 # source files. Fool the emitter's str() calls by setting 501 # up a temporary builder on the new targets. 502 new_targets = [] 503 for t in tlist: 504 if not t.is_derived(): 505 t.builder_set(self) 506 new_targets.append(t) 507 508 orig_tlist = tlist[:] 509 orig_slist = slist[:] 510 511 target, source = self.emitter(target=tlist, source=slist, env=env) 512 513 # Now delete the temporary builders that we attached to any 514 # new targets, so that _node_errors() doesn't do weird stuff 515 # to them because it thinks they already have builders. 516 for t in new_targets: 517 if t.builder is self: 518 # Only delete the temporary builder if the emitter 519 # didn't change it on us. 520 t.builder_set(None) 521 522 # Have to call arg2nodes yet again, since it is legal for 523 # emitters to spit out strings as well as Node instances. 524 tlist = env.arg2nodes(target, target_factory, 525 target=orig_tlist, source=orig_slist) 526 slist = env.arg2nodes(source, source_factory, 527 target=orig_tlist, source=orig_slist) 528 529 return tlist, slist
530
531 - def _execute(self, env, target, source, overwarn={}, executor_kw={}):
532 # We now assume that target and source are lists or None. 533 if self.src_builder: 534 source = self.src_builder_sources(env, source, overwarn) 535 536 if self.single_source and len(source) > 1 and target is None: 537 result = [] 538 if target is None: target = [None]*len(source) 539 for tgt, src in zip(target, source): 540 if not tgt is None: tgt = [tgt] 541 if not src is None: src = [src] 542 result.extend(self._execute(env, tgt, src, overwarn)) 543 return SCons.Node.NodeList(result) 544 545 overwarn.warn() 546 547 tlist, slist = self._create_nodes(env, target, source) 548 549 # Check for errors with the specified target/source lists. 550 _node_errors(self, env, tlist, slist) 551 552 # The targets are fine, so find or make the appropriate Executor to 553 # build this particular list of targets from this particular list of 554 # sources. 555 556 executor = None 557 key = None 558 559 if self.multi: 560 try: 561 executor = tlist[0].get_executor(create = 0) 562 except (AttributeError, IndexError): 563 pass 564 else: 565 executor.add_sources(slist) 566 567 if executor is None: 568 if not self.action: 569 fmt = "Builder %s must have an action to build %s." 570 raise UserError(fmt % (self.get_name(env or self.env), 571 list(map(str,tlist)))) 572 key = self.action.batch_key(env or self.env, tlist, slist) 573 if key: 574 try: 575 executor = SCons.Executor.GetBatchExecutor(key) 576 except KeyError: 577 pass 578 else: 579 executor.add_batch(tlist, slist) 580 581 if executor is None: 582 executor = SCons.Executor.Executor(self.action, env, [], 583 tlist, slist, executor_kw) 584 if key: 585 SCons.Executor.AddBatchExecutor(key, executor) 586 587 # Now set up the relevant information in the target Nodes themselves. 588 for t in tlist: 589 t.cwd = env.fs.getcwd() 590 t.builder_set(self) 591 t.env_set(env) 592 t.add_source(slist) 593 t.set_executor(executor) 594 t.set_explicit(self.is_explicit) 595 596 return SCons.Node.NodeList(tlist)
597
598 - def __call__(self, env, target=None, source=None, chdir=_null, **kw):
599 # We now assume that target and source are lists or None. 600 # The caller (typically Environment.BuilderWrapper) is 601 # responsible for converting any scalar values to lists. 602 if chdir is _null: 603 ekw = self.executor_kw 604 else: 605 ekw = self.executor_kw.copy() 606 ekw['chdir'] = chdir 607 if kw: 608 if 'srcdir' in kw: 609 def prependDirIfRelative(f, srcdir=kw['srcdir']): 610 import os.path 611 if SCons.Util.is_String(f) and not os.path.isabs(f): 612 f = os.path.join(srcdir, f) 613 return f
614 if not SCons.Util.is_List(source): 615 source = [source] 616 source = list(map(prependDirIfRelative, source)) 617 del kw['srcdir'] 618 if self.overrides: 619 env_kw = self.overrides.copy() 620 env_kw.update(kw) 621 else: 622 env_kw = kw 623 else: 624 env_kw = self.overrides 625 env = env.Override(env_kw) 626 return self._execute(env, target, source, OverrideWarner(kw), ekw)
627
628 - def adjust_suffix(self, suff):
629 if suff and not suff[0] in [ '.', '_', '$' ]: 630 return '.' + suff 631 return suff
632
633 - def get_prefix(self, env, sources=[]):
634 prefix = self.prefix 635 if callable(prefix): 636 prefix = prefix(env, sources) 637 return env.subst(prefix)
638
639 - def set_suffix(self, suffix):
640 if not callable(suffix): 641 suffix = self.adjust_suffix(suffix) 642 self.suffix = suffix
643
644 - def get_suffix(self, env, sources=[]):
645 suffix = self.suffix 646 if callable(suffix): 647 suffix = suffix(env, sources) 648 return env.subst(suffix)
649
650 - def set_src_suffix(self, src_suffix):
651 if not src_suffix: 652 src_suffix = [] 653 elif not SCons.Util.is_List(src_suffix): 654 src_suffix = [ src_suffix ] 655 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
656
657 - def get_src_suffix(self, env):
658 """Get the first src_suffix in the list of src_suffixes.""" 659 ret = self.src_suffixes(env) 660 if not ret: 661 return '' 662 return ret[0]
663
664 - def add_emitter(self, suffix, emitter):
665 """Add a suffix-emitter mapping to this Builder. 666 667 This assumes that emitter has been initialized with an 668 appropriate dictionary type, and will throw a TypeError if 669 not, so the caller is responsible for knowing that this is an 670 appropriate method to call for the Builder in question. 671 """ 672 self.emitter[suffix] = emitter
673
674 - def add_src_builder(self, builder):
675 """ 676 Add a new Builder to the list of src_builders. 677 678 This requires wiping out cached values so that the computed 679 lists of source suffixes get re-calculated. 680 """ 681 self._memo = {} 682 self.src_builder.append(builder)
683
684 - def _get_sdict(self, env):
685 """ 686 Returns a dictionary mapping all of the source suffixes of all 687 src_builders of this Builder to the underlying Builder that 688 should be called first. 689 690 This dictionary is used for each target specified, so we save a 691 lot of extra computation by memoizing it for each construction 692 environment. 693 694 Note that this is re-computed each time, not cached, because there 695 might be changes to one of our source Builders (or one of their 696 source Builders, and so on, and so on...) that we can't "see." 697 698 The underlying methods we call cache their computed values, 699 though, so we hope repeatedly aggregating them into a dictionary 700 like this won't be too big a hit. We may need to look for a 701 better way to do this if performance data show this has turned 702 into a significant bottleneck. 703 """ 704 sdict = {} 705 for bld in self.get_src_builders(env): 706 for suf in bld.src_suffixes(env): 707 sdict[suf] = bld 708 return sdict
709
710 - def src_builder_sources(self, env, source, overwarn={}):
711 sdict = self._get_sdict(env) 712 713 src_suffixes = self.src_suffixes(env) 714 715 lengths = list(set(map(len, src_suffixes))) 716 717 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): 718 node_suffixes = [name[-l:] for l in lengths] 719 for suf in src_suffixes: 720 if suf in node_suffixes: 721 return suf 722 return None
723 724 result = [] 725 for s in SCons.Util.flatten(source): 726 if SCons.Util.is_String(s): 727 match_suffix = match_src_suffix(env.subst(s)) 728 if not match_suffix and not '.' in s: 729 src_suf = self.get_src_suffix(env) 730 s = self._adjustixes(s, None, src_suf)[0] 731 else: 732 match_suffix = match_src_suffix(s.name) 733 if match_suffix: 734 try: 735 bld = sdict[match_suffix] 736 except KeyError: 737 result.append(s) 738 else: 739 tlist = bld._execute(env, None, [s], overwarn) 740 # If the subsidiary Builder returned more than one 741 # target, then filter out any sources that this 742 # Builder isn't capable of building. 743 if len(tlist) > 1: 744 tlist = [t for t in tlist if match_src_suffix(t.name)] 745 result.extend(tlist) 746 else: 747 result.append(s) 748 749 source_factory = env.get_factory(self.source_factory) 750 751 return env.arg2nodes(result, source_factory) 752
753 - def _get_src_builders_key(self, env):
754 return id(env)
755 756 @SCons.Memoize.CountDictCall(_get_src_builders_key)
757 - def get_src_builders(self, env):
758 """ 759 Returns the list of source Builders for this Builder. 760 761 This exists mainly to look up Builders referenced as 762 strings in the 'BUILDER' variable of the construction 763 environment and cache the result. 764 """ 765 memo_key = id(env) 766 try: 767 memo_dict = self._memo['get_src_builders'] 768 except KeyError: 769 memo_dict = {} 770 self._memo['get_src_builders'] = memo_dict 771 else: 772 try: 773 return memo_dict[memo_key] 774 except KeyError: 775 pass 776 777 builders = [] 778 for bld in self.src_builder: 779 if SCons.Util.is_String(bld): 780 try: 781 bld = env['BUILDERS'][bld] 782 except KeyError: 783 continue 784 builders.append(bld) 785 786 memo_dict[memo_key] = builders 787 return builders
788
789 - def _subst_src_suffixes_key(self, env):
790 return id(env)
791 792 @SCons.Memoize.CountDictCall(_subst_src_suffixes_key)
793 - def subst_src_suffixes(self, env):
794 """ 795 The suffix list may contain construction variable expansions, 796 so we have to evaluate the individual strings. To avoid doing 797 this over and over, we memoize the results for each construction 798 environment. 799 """ 800 memo_key = id(env) 801 try: 802 memo_dict = self._memo['subst_src_suffixes'] 803 except KeyError: 804 memo_dict = {} 805 self._memo['subst_src_suffixes'] = memo_dict 806 else: 807 try: 808 return memo_dict[memo_key] 809 except KeyError: 810 pass 811 suffixes = [env.subst(x) for x in self.src_suffix] 812 memo_dict[memo_key] = suffixes 813 return suffixes
814
815 - def src_suffixes(self, env):
816 """ 817 Returns the list of source suffixes for all src_builders of this 818 Builder. 819 820 This is essentially a recursive descent of the src_builder "tree." 821 (This value isn't cached because there may be changes in a 822 src_builder many levels deep that we can't see.) 823 """ 824 sdict = {} 825 suffixes = self.subst_src_suffixes(env) 826 for s in suffixes: 827 sdict[s] = 1 828 for builder in self.get_src_builders(env): 829 for s in builder.src_suffixes(env): 830 if s not in sdict: 831 sdict[s] = 1 832 suffixes.append(s) 833 return suffixes
834
835 -class CompositeBuilder(SCons.Util.Proxy):
836 """A Builder Proxy whose main purpose is to always have 837 a DictCmdGenerator as its action, and to provide access 838 to the DictCmdGenerator's add_action() method. 839 """ 840
841 - def __init__(self, builder, cmdgen):
842 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.CompositeBuilder') 843 SCons.Util.Proxy.__init__(self, builder) 844 845 # cmdgen should always be an instance of DictCmdGenerator. 846 self.cmdgen = cmdgen 847 self.builder = builder
848 849 __call__ = SCons.Util.Delegate('__call__') 850
851 - def add_action(self, suffix, action):
852 self.cmdgen.add_action(suffix, action) 853 self.set_src_suffix(self.cmdgen.src_suffixes())
854
855 -def is_a_Builder(obj):
856 """"Returns True if the specified obj is one of our Builder classes. 857 858 The test is complicated a bit by the fact that CompositeBuilder 859 is a proxy, not a subclass of BuilderBase. 860 """ 861 return (isinstance(obj, BuilderBase) 862 or isinstance(obj, CompositeBuilder) 863 or callable(obj))
864 865 # Local Variables: 866 # tab-width:4 867 # indent-tabs-mode:nil 868 # End: 869 # vim: set expandtab tabstop=4 shiftwidth=4: 870