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