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

Source Code for Module SCons.Builder

  1  """ 
  2  SCons.Builder 
  3   
  4  Builder object subsystem. 
  5   
  6  A Builder object is a callable that encapsulates information about how 
  7  to execute actions to create a target Node (file) from source Nodes 
  8  (files), and how to create those dependencies for tracking. 
  9   
 10  The main entry point here is the Builder() factory method.  This provides 
 11  a procedural interface that creates the right underlying Builder object 
 12  based on the keyword arguments supplied and the types of the arguments. 
 13   
 14  The goal is for this external interface to be simple enough that the 
 15  vast majority of users can create new Builders as necessary to support 
 16  building new types of files in their configurations, without having to 
 17  dive any deeper into this subsystem. 
 18   
 19  The base class here is BuilderBase.  This is a concrete base class which 
 20  does, in fact, represent the Builder objects that we (or users) create. 
 21   
 22  There is also a proxy that looks like a Builder: 
 23   
 24      CompositeBuilder 
 25   
 26          This proxies for a Builder with an action that is actually a 
 27          dictionary that knows how to map file suffixes to a specific 
 28          action.  This is so that we can invoke different actions 
 29          (compilers, compile options) for different flavors of source 
 30          files. 
 31   
 32  Builders and their proxies have the following public interface methods 
 33  used by other modules: 
 34   
 35      - __call__() 
 36          THE public interface.  Calling a Builder object (with the 
 37          use of internal helper methods) sets up the target and source 
 38          dependencies, appropriate mapping to a specific action, and the 
 39          environment manipulation necessary for overridden construction 
 40          variable.  This also takes care of warning about possible mistakes 
 41          in keyword arguments. 
 42   
 43      - add_emitter() 
 44          Adds an emitter for a specific file suffix, used by some Tool 
 45          modules to specify that (for example) a yacc invocation on a .y 
 46          can create a .h *and* a .c file. 
 47   
 48      - add_action() 
 49          Adds an action for a specific file suffix, heavily used by 
 50          Tool modules to add their specific action(s) for turning 
 51          a source file into an object file to the global static 
 52          and shared object file Builders. 
 53   
 54  There are the following methods for internal use within this module: 
 55   
 56      - _execute() 
 57          The internal method that handles the heavily lifting when a 
 58          Builder is called.  This is used so that the __call__() methods 
 59          can set up warning about possible mistakes in keyword-argument 
 60          overrides, and *then* execute all of the steps necessary so that 
 61          the warnings only occur once. 
 62   
 63      - get_name() 
 64          Returns the Builder's name within a specific Environment, 
 65          primarily used to try to return helpful information in error 
 66          messages. 
 67   
 68      - adjust_suffix() 
 69      - get_prefix() 
 70      - get_suffix() 
 71      - get_src_suffix() 
 72      - set_src_suffix() 
 73          Miscellaneous stuff for handling the prefix and suffix 
 74          manipulation we use in turning source file names into target 
 75          file names. 
 76   
 77  """ 
 78   
 79  # 
 80  # Copyright (c) 2001 - 2017 The SCons Foundation 
 81  # 
 82  # Permission is hereby granted, free of charge, to any person obtaining 
 83  # a copy of this software and associated documentation files (the 
 84  # "Software"), to deal in the Software without restriction, including 
 85  # without limitation the rights to use, copy, modify, merge, publish, 
 86  # distribute, sublicense, and/or sell copies of the Software, and to 
 87  # permit persons to whom the Software is furnished to do so, subject to 
 88  # the following conditions: 
 89  # 
 90  # The above copyright notice and this permission notice shall be included 
 91  # in all copies or substantial portions of the Software. 
 92  # 
 93  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
 94  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
 95  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 96  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 97  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
 98  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
 99  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
100   
101  __revision__ = "src/engine/SCons/Builder.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" 
102   
103  import collections 
104   
105  import SCons.Action 
106  import SCons.Debug 
107  from SCons.Debug import logInstanceCreation 
108  from SCons.Errors import InternalError, UserError 
109  import SCons.Executor 
110  import SCons.Memoize 
111  import SCons.Util 
112  import SCons.Warnings 
113 114 -class _Null(object):
115 pass
116 117 _null = _Null
118 119 -def match_splitext(path, suffixes = []):
120 if suffixes: 121 matchsuf = [S for S in suffixes if path[-len(S):] == S] 122 if matchsuf: 123 suf = max([(len(_f),_f) for _f in matchsuf])[1] 124 return [path[:-len(suf)], path[-len(suf):]] 125 return SCons.Util.splitext(path)
126
127 -class DictCmdGenerator(SCons.Util.Selector):
128 """This is a callable class that can be used as a 129 command generator function. It holds on to a dictionary 130 mapping file suffixes to Actions. It uses that dictionary 131 to return the proper action based on the file suffix of 132 the source file.""" 133
134 - def __init__(self, dict=None, source_ext_match=1):
135 SCons.Util.Selector.__init__(self, dict) 136 self.source_ext_match = source_ext_match
137
138 - def src_suffixes(self):
139 return list(self.keys())
140
141 - def add_action(self, suffix, action):
142 """Add a suffix-action pair to the mapping. 143 """ 144 self[suffix] = action
145
146 - def __call__(self, target, source, env, for_signature):
147 if not source: 148 return [] 149 150 if self.source_ext_match: 151 suffixes = self.src_suffixes() 152 ext = None 153 for src in map(str, source): 154 my_ext = match_splitext(src, suffixes)[1] 155 if ext and my_ext != ext: 156 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" 157 % (repr(list(map(str, target))), src, ext, my_ext)) 158 ext = my_ext 159 else: 160 ext = match_splitext(str(source[0]), self.src_suffixes())[1] 161 162 if not ext: 163 #return ext 164 raise UserError("While building `%s': " 165 "Cannot deduce file extension from source files: %s" 166 % (repr(list(map(str, target))), repr(list(map(str, source))))) 167 168 try: 169 ret = SCons.Util.Selector.__call__(self, env, source, ext) 170 except KeyError as e: 171 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[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(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(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(collections.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(collections.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 collections.UserDict.__init__(self, dict) 228 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner') 229 self.already_warned = None
230 - def warn(self):
231 if self.already_warned: 232 return 233 for k in list(self.keys()): 234 if k in misleading_keywords: 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 'generator' in kw: 244 if 'action' in kw: 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 'action' in kw: 249 source_ext_match = kw.get('source_ext_match', 1) 250 if 'source_ext_match' in kw: 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 'emitter' in kw: 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 = 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 = t.builder.action.get_contents(tlist, slist, t.env) 297 contents = builder.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\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8')) 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, list(map(str, t.get_executor().get_all_targets())), list(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, list(map(str, t.sources)), list(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" % (list(map(str,tlist)), list(map(str,slist))))
320
321 -class EmitterProxy(object):
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 emitter in env: 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 __eq__(self, other):
349 return self.var == other.var
350
351 - def __lt__(self, other):
352 return self.var < other.var
353
354 -class BuilderBase(object):
355 """Base class for Builders, objects that create output 356 nodes (files) from input nodes (files). 357 """ 358
359 - def __init__(self, action = None, 360 prefix = '', 361 suffix = '', 362 src_suffix = '', 363 target_factory = None, 364 source_factory = None, 365 target_scanner = None, 366 source_scanner = None, 367 emitter = None, 368 multi = 0, 369 env = None, 370 single_source = 0, 371 name = None, 372 chdir = _null, 373 is_explicit = 1, 374 src_builder = None, 375 ensure_suffix = False, 376 **overrides):
377 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase') 378 self._memo = {} 379 self.action = action 380 self.multi = multi 381 if SCons.Util.is_Dict(prefix): 382 prefix = CallableSelector(prefix) 383 self.prefix = prefix 384 if SCons.Util.is_Dict(suffix): 385 suffix = CallableSelector(suffix) 386 self.env = env 387 self.single_source = single_source 388 if 'overrides' in overrides: 389 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 390 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ 391 "\tspecify the items as keyword arguments to the Builder() call instead.") 392 overrides.update(overrides['overrides']) 393 del overrides['overrides'] 394 if 'scanner' in overrides: 395 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, 396 "The \"scanner\" keyword to Builder() creation has been deprecated;\n" 397 "\tuse: source_scanner or target_scanner as appropriate.") 398 del overrides['scanner'] 399 self.overrides = overrides 400 401 self.set_suffix(suffix) 402 self.set_src_suffix(src_suffix) 403 self.ensure_suffix = ensure_suffix 404 405 self.target_factory = target_factory 406 self.source_factory = source_factory 407 self.target_scanner = target_scanner 408 self.source_scanner = source_scanner 409 410 self.emitter = emitter 411 412 # Optional Builder name should only be used for Builders 413 # that don't get attached to construction environments. 414 if name: 415 self.name = name 416 self.executor_kw = {} 417 if not chdir is _null: 418 self.executor_kw['chdir'] = chdir 419 self.is_explicit = is_explicit 420 421 if src_builder is None: 422 src_builder = [] 423 elif not SCons.Util.is_List(src_builder): 424 src_builder = [ src_builder ] 425 self.src_builder = src_builder
426
427 - def __nonzero__(self):
428 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
429
430 - def __bool__(self):
431 return self.__nonzero__()
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 __eq__(self, other):
451 return 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 'chdir' in ekw and SCons.Util.is_String(ekw['chdir']): 615 ekw['chdir'] = env.subst(ekw['chdir']) 616 if kw: 617 if 'srcdir' in kw: 618 def prependDirIfRelative(f, srcdir=kw['srcdir']): 619 import os.path 620 if SCons.Util.is_String(f) and not os.path.isabs(f): 621 f = os.path.join(srcdir, f) 622 return f
623 if not SCons.Util.is_List(source): 624 source = [source] 625 source = list(map(prependDirIfRelative, source)) 626 del kw['srcdir'] 627 if self.overrides: 628 env_kw = self.overrides.copy() 629 env_kw.update(kw) 630 else: 631 env_kw = kw 632 else: 633 env_kw = self.overrides 634 env = env.Override(env_kw) 635 return self._execute(env, target, source, OverrideWarner(kw), ekw)
636
637 - def adjust_suffix(self, suff):
638 if suff and not suff[0] in [ '.', '_', '$' ]: 639 return '.' + suff 640 return suff
641
642 - def get_prefix(self, env, sources=[]):
643 prefix = self.prefix 644 if callable(prefix): 645 prefix = prefix(env, sources) 646 return env.subst(prefix)
647
648 - def set_suffix(self, suffix):
649 if not callable(suffix): 650 suffix = self.adjust_suffix(suffix) 651 self.suffix = suffix
652
653 - def get_suffix(self, env, sources=[]):
654 suffix = self.suffix 655 if callable(suffix): 656 suffix = suffix(env, sources) 657 return env.subst(suffix)
658
659 - def set_src_suffix(self, src_suffix):
660 if not src_suffix: 661 src_suffix = [] 662 elif not SCons.Util.is_List(src_suffix): 663 src_suffix = [ src_suffix ] 664 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
665
666 - def get_src_suffix(self, env):
667 """Get the first src_suffix in the list of src_suffixes.""" 668 ret = self.src_suffixes(env) 669 if not ret: 670 return '' 671 return ret[0]
672
673 - def add_emitter(self, suffix, emitter):
674 """Add a suffix-emitter mapping to this Builder. 675 676 This assumes that emitter has been initialized with an 677 appropriate dictionary type, and will throw a TypeError if 678 not, so the caller is responsible for knowing that this is an 679 appropriate method to call for the Builder in question. 680 """ 681 self.emitter[suffix] = emitter
682
683 - def add_src_builder(self, builder):
684 """ 685 Add a new Builder to the list of src_builders. 686 687 This requires wiping out cached values so that the computed 688 lists of source suffixes get re-calculated. 689 """ 690 self._memo = {} 691 self.src_builder.append(builder)
692
693 - def _get_sdict(self, env):
694 """ 695 Returns a dictionary mapping all of the source suffixes of all 696 src_builders of this Builder to the underlying Builder that 697 should be called first. 698 699 This dictionary is used for each target specified, so we save a 700 lot of extra computation by memoizing it for each construction 701 environment. 702 703 Note that this is re-computed each time, not cached, because there 704 might be changes to one of our source Builders (or one of their 705 source Builders, and so on, and so on...) that we can't "see." 706 707 The underlying methods we call cache their computed values, 708 though, so we hope repeatedly aggregating them into a dictionary 709 like this won't be too big a hit. We may need to look for a 710 better way to do this if performance data show this has turned 711 into a significant bottleneck. 712 """ 713 sdict = {} 714 for bld in self.get_src_builders(env): 715 for suf in bld.src_suffixes(env): 716 sdict[suf] = bld 717 return sdict
718
719 - def src_builder_sources(self, env, source, overwarn={}):
720 sdict = self._get_sdict(env) 721 722 src_suffixes = self.src_suffixes(env) 723 724 lengths = list(set(map(len, src_suffixes))) 725 726 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): 727 node_suffixes = [name[-l:] for l in lengths] 728 for suf in src_suffixes: 729 if suf in node_suffixes: 730 return suf 731 return None
732 733 result = [] 734 for s in SCons.Util.flatten(source): 735 if SCons.Util.is_String(s): 736 match_suffix = match_src_suffix(env.subst(s)) 737 if not match_suffix and not '.' in s: 738 src_suf = self.get_src_suffix(env) 739 s = self._adjustixes(s, None, src_suf)[0] 740 else: 741 match_suffix = match_src_suffix(s.name) 742 if match_suffix: 743 try: 744 bld = sdict[match_suffix] 745 except KeyError: 746 result.append(s) 747 else: 748 tlist = bld._execute(env, None, [s], overwarn) 749 # If the subsidiary Builder returned more than one 750 # target, then filter out any sources that this 751 # Builder isn't capable of building. 752 if len(tlist) > 1: 753 tlist = [t for t in tlist if match_src_suffix(t.name)] 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 @SCons.Memoize.CountDictCall(_get_src_builders_key)
766 - def get_src_builders(self, env):
767 """ 768 Returns the list of source Builders for this Builder. 769 770 This exists mainly to look up Builders referenced as 771 strings in the 'BUILDER' variable of the construction 772 environment and cache the result. 773 """ 774 memo_key = id(env) 775 try: 776 memo_dict = self._memo['get_src_builders'] 777 except KeyError: 778 memo_dict = {} 779 self._memo['get_src_builders'] = memo_dict 780 else: 781 try: 782 return memo_dict[memo_key] 783 except KeyError: 784 pass 785 786 builders = [] 787 for bld in self.src_builder: 788 if SCons.Util.is_String(bld): 789 try: 790 bld = env['BUILDERS'][bld] 791 except KeyError: 792 continue 793 builders.append(bld) 794 795 memo_dict[memo_key] = builders 796 return builders
797
798 - def _subst_src_suffixes_key(self, env):
799 return id(env)
800 801 @SCons.Memoize.CountDictCall(_subst_src_suffixes_key)
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 if 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