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