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