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