1# MIT License 2# 3# Copyright The SCons Foundation 4# 5# Permission is hereby granted, free of charge, to any person obtaining 6# a copy of this software and associated documentation files (the 7# "Software"), to deal in the Software without restriction, including 8# without limitation the rights to use, copy, modify, merge, publish, 9# distribute, sublicense, and/or sell copies of the Software, and to 10# permit persons to whom the Software is furnished to do so, subject to 11# the following conditions: 12# 13# The above copyright notice and this permission notice shall be included 14# in all copies or substantial portions of the Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 17# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 18# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24""" 25SCons.Builder 26 27Builder object subsystem. 28 29A Builder object is a callable that encapsulates information about how 30to execute actions to create a target Node (file) from source Nodes 31(files), and how to create those dependencies for tracking. 32 33The main entry point here is the Builder() factory method. This provides 34a procedural interface that creates the right underlying Builder object 35based on the keyword arguments supplied and the types of the arguments. 36 37The goal is for this external interface to be simple enough that the 38vast majority of users can create new Builders as necessary to support 39building new types of files in their configurations, without having to 40dive any deeper into this subsystem. 41 42The base class here is BuilderBase. This is a concrete base class which 43does, in fact, represent the Builder objects that we (or users) create. 44 45There is also a proxy that looks like a Builder: 46 47 CompositeBuilder 48 49 This proxies for a Builder with an action that is actually a 50 dictionary that knows how to map file suffixes to a specific 51 action. This is so that we can invoke different actions 52 (compilers, compile options) for different flavors of source 53 files. 54 55Builders and their proxies have the following public interface methods 56used by other modules: 57 58 - __call__() 59 THE public interface. Calling a Builder object (with the 60 use of internal helper methods) sets up the target and source 61 dependencies, appropriate mapping to a specific action, and the 62 environment manipulation necessary for overridden construction 63 variable. This also takes care of warning about possible mistakes 64 in keyword arguments. 65 66 - add_emitter() 67 Adds an emitter for a specific file suffix, used by some Tool 68 modules to specify that (for example) a yacc invocation on a .y 69 can create a .h *and* a .c file. 70 71 - add_action() 72 Adds an action for a specific file suffix, heavily used by 73 Tool modules to add their specific action(s) for turning 74 a source file into an object file to the global static 75 and shared object file Builders. 76 77There are the following methods for internal use within this module: 78 79 - _execute() 80 The internal method that handles the heavily lifting when a 81 Builder is called. This is used so that the __call__() methods 82 can set up warning about possible mistakes in keyword-argument 83 overrides, and *then* execute all of the steps necessary so that 84 the warnings only occur once. 85 86 - get_name() 87 Returns the Builder's name within a specific Environment, 88 primarily used to try to return helpful information in error 89 messages. 90 91 - adjust_suffix() 92 - get_prefix() 93 - get_suffix() 94 - get_src_suffix() 95 - set_src_suffix() 96 Miscellaneous stuff for handling the prefix and suffix 97 manipulation we use in turning source file names into target 98 file names. 99 100""" 101 102from collections import UserDict, UserList 103 104import SCons.Action 105import SCons.Debug 106import SCons.Executor 107import SCons.Memoize 108import SCons.Util 109import SCons.Warnings 110from SCons.Debug import logInstanceCreation 111from SCons.Errors import InternalError, UserError 112 113class _Null: 114 pass 115 116_null = _Null 117 118def match_splitext(path, suffixes = []): 119 if suffixes: 120 matchsuf = [S for S in suffixes if path[-len(S):] == S] 121 if matchsuf: 122 suf = max([(len(_f),_f) for _f in matchsuf])[1] 123 return [path[:-len(suf)], path[-len(suf):]] 124 return SCons.Util.splitext(path) 125 126class DictCmdGenerator(SCons.Util.Selector): 127 """This is a callable class that can be used as a 128 command generator function. It holds on to a dictionary 129 mapping file suffixes to Actions. It uses that dictionary 130 to return the proper action based on the file suffix of 131 the source file.""" 132 133 def __init__(self, dict=None, source_ext_match=1): 134 SCons.Util.Selector.__init__(self, dict) 135 self.source_ext_match = source_ext_match 136 137 def src_suffixes(self): 138 return list(self.keys()) 139 140 def add_action(self, suffix, action): 141 """Add a suffix-action pair to the mapping. 142 """ 143 self[suffix] = action 144 145 def __call__(self, target, source, env, for_signature): 146 if not source: 147 return [] 148 149 if self.source_ext_match: 150 suffixes = self.src_suffixes() 151 ext = None 152 for src in map(str, source): 153 my_ext = match_splitext(src, suffixes)[1] 154 if ext and my_ext != ext: 155 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" 156 % (repr(list(map(str, target))), src, ext, my_ext)) 157 ext = my_ext 158 else: 159 ext = match_splitext(str(source[0]), self.src_suffixes())[1] 160 161 if not ext: 162 #return ext 163 raise UserError("While building `%s': " 164 "Cannot deduce file extension from source files: %s" 165 % (repr(list(map(str, target))), repr(list(map(str, source))))) 166 167 try: 168 ret = SCons.Util.Selector.__call__(self, env, source, ext) 169 except KeyError as e: 170 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) 171 if ret is None: 172 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." % \ 173 (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) 174 return ret 175 176class CallableSelector(SCons.Util.Selector): 177 """A callable dictionary that will, in turn, call the value it 178 finds if it can.""" 179 def __call__(self, env, source): 180 value = SCons.Util.Selector.__call__(self, env, source) 181 if callable(value): 182 value = value(env, source) 183 return value 184 185class DictEmitter(SCons.Util.Selector): 186 """A callable dictionary that maps file suffixes to emitters. 187 When called, it finds the right emitter in its dictionary for the 188 suffix of the first source file, and calls that emitter to get the 189 right lists of targets and sources to return. If there's no emitter 190 for the suffix in its dictionary, the original target and source are 191 returned. 192 """ 193 def __call__(self, target, source, env): 194 emitter = SCons.Util.Selector.__call__(self, env, source) 195 if emitter: 196 target, source = emitter(target, source, env) 197 return (target, source) 198 199class ListEmitter(UserList): 200 """A callable list of emitters that calls each in sequence, 201 returning the result. 202 """ 203 def __call__(self, target, source, env): 204 for e in self.data: 205 target, source = e(target, source, env) 206 return (target, source) 207 208# These are a common errors when calling a Builder; 209# they are similar to the 'target' and 'source' keyword args to builders, 210# so we issue warnings when we see them. The warnings can, of course, 211# be disabled. 212misleading_keywords = { 213 'targets' : 'target', 214 'sources' : 'source', 215} 216 217class OverrideWarner(UserDict): 218 """A class for warning about keyword arguments that we use as 219 overrides in a Builder call. 220 221 This class exists to handle the fact that a single Builder call 222 can actually invoke multiple builders. This class only emits the 223 warnings once, no matter how many Builders are invoked. 224 """ 225 def __init__(self, dict): 226 UserDict.__init__(self, dict) 227 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner') 228 self.already_warned = None 229 def warn(self): 230 if self.already_warned: 231 return 232 for k in self.keys(): 233 if k in misleading_keywords: 234 alt = misleading_keywords[k] 235 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) 236 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) 237 self.already_warned = 1 238 239def Builder(**kw): 240 """A factory for builder objects.""" 241 composite = None 242 if 'generator' in kw: 243 if 'action' in kw: 244 raise UserError("You must not specify both an action and a generator.") 245 kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) 246 del kw['generator'] 247 elif 'action' in kw: 248 source_ext_match = kw.get('source_ext_match', 1) 249 if 'source_ext_match' in kw: 250 del kw['source_ext_match'] 251 if SCons.Util.is_Dict(kw['action']): 252 composite = DictCmdGenerator(kw['action'], source_ext_match) 253 kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) 254 kw['src_suffix'] = composite.src_suffixes() 255 else: 256 kw['action'] = SCons.Action.Action(kw['action']) 257 258 if 'emitter' in kw: 259 emitter = kw['emitter'] 260 if SCons.Util.is_String(emitter): 261 # This allows users to pass in an Environment 262 # variable reference (like "$FOO") as an emitter. 263 # We will look in that Environment variable for 264 # a callable to use as the actual emitter. 265 var = SCons.Util.get_environment_var(emitter) 266 if not var: 267 raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) 268 kw['emitter'] = EmitterProxy(var) 269 elif SCons.Util.is_Dict(emitter): 270 kw['emitter'] = DictEmitter(emitter) 271 elif SCons.Util.is_List(emitter): 272 kw['emitter'] = ListEmitter(emitter) 273 274 result = BuilderBase(**kw) 275 276 if composite is not None: 277 result = CompositeBuilder(result, composite) 278 279 return result 280 281def _node_errors(builder, env, tlist, slist): 282 """Validate that the lists of target and source nodes are 283 legal for this builder and environment. Raise errors or 284 issue warnings as appropriate. 285 """ 286 287 # First, figure out if there are any errors in the way the targets 288 # were specified. 289 for t in tlist: 290 if t.side_effect: 291 raise UserError("Multiple ways to build the same target were specified for: %s" % t) 292 if t.has_explicit_builder(): 293 # Check for errors when the environments are different 294 # No error if environments are the same Environment instance 295 if (t.env is not None and t.env is not env and 296 # Check OverrideEnvironment case - no error if wrapped Environments 297 # are the same instance, and overrides lists match 298 not (getattr(t.env, '__subject', 0) is getattr(env, '__subject', 1) and 299 getattr(t.env, 'overrides', 0) == getattr(env, 'overrides', 1) and 300 not builder.multi)): 301 action = t.builder.action 302 t_contents = t.builder.action.get_contents(tlist, slist, t.env) 303 contents = builder.action.get_contents(tlist, slist, env) 304 305 if t_contents == contents: 306 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)) 307 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) 308 else: 309 try: 310 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')) 311 except UnicodeDecodeError: 312 msg = "Two environments with different actions were specified for the same target: %s"%t 313 raise UserError(msg) 314 if builder.multi: 315 if t.builder != builder: 316 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) 317 raise UserError(msg) 318 # TODO(batch): list constructed each time! 319 if t.get_executor().get_all_targets() != tlist: 320 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))) 321 raise UserError(msg) 322 elif t.sources != slist: 323 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))) 324 raise UserError(msg) 325 326 if builder.single_source: 327 if len(slist) > 1: 328 raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) 329 330class EmitterProxy: 331 """This is a callable class that can act as a 332 Builder emitter. It holds on to a string that 333 is a key into an Environment dictionary, and will 334 look there at actual build time to see if it holds 335 a callable. If so, we will call that as the actual 336 emitter.""" 337 def __init__(self, var): 338 self.var = SCons.Util.to_String(var) 339 340 def __call__(self, target, source, env): 341 emitter = self.var 342 343 # Recursively substitute the variable. 344 # We can't use env.subst() because it deals only 345 # in strings. Maybe we should change that? 346 while SCons.Util.is_String(emitter) and emitter in env: 347 emitter = env[emitter] 348 if callable(emitter): 349 target, source = emitter(target, source, env) 350 elif SCons.Util.is_List(emitter): 351 for e in emitter: 352 target, source = e(target, source, env) 353 354 return (target, source) 355 356 357 def __eq__(self, other): 358 return self.var == other.var 359 360 def __lt__(self, other): 361 return self.var < other.var 362 363class BuilderBase: 364 """Base class for Builders, objects that create output 365 nodes (files) from input nodes (files). 366 """ 367 368 def __init__(self, action = None, 369 prefix = '', 370 suffix = '', 371 src_suffix = '', 372 target_factory = None, 373 source_factory = None, 374 target_scanner = None, 375 source_scanner = None, 376 emitter = None, 377 multi = 0, 378 env = None, 379 single_source = 0, 380 name = None, 381 chdir = _null, 382 is_explicit = 1, 383 src_builder = None, 384 ensure_suffix = False, 385 **overrides): 386 if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase') 387 self._memo = {} 388 self.action = action 389 self.multi = multi 390 if SCons.Util.is_Dict(prefix): 391 prefix = CallableSelector(prefix) 392 self.prefix = prefix 393 if SCons.Util.is_Dict(suffix): 394 suffix = CallableSelector(suffix) 395 self.env = env 396 self.single_source = single_source 397 if 'overrides' in overrides: 398 msg = "The \"overrides\" keyword to Builder() creation has been removed;\n" +\ 399 "\tspecify the items as keyword arguments to the Builder() call instead." 400 raise TypeError(msg) 401 if 'scanner' in overrides: 402 msg = "The \"scanner\" keyword to Builder() creation has been removed;\n" +\ 403 "\tuse: source_scanner or target_scanner as appropriate." 404 raise TypeError(msg) 405 self.overrides = overrides 406 407 self.set_suffix(suffix) 408 self.set_src_suffix(src_suffix) 409 self.ensure_suffix = ensure_suffix 410 411 self.target_factory = target_factory 412 self.source_factory = source_factory 413 self.target_scanner = target_scanner 414 self.source_scanner = source_scanner 415 416 self.emitter = emitter 417 418 # Optional Builder name should only be used for Builders 419 # that don't get attached to construction environments. 420 if name: 421 self.name = name 422 self.executor_kw = {} 423 if chdir is not _null: 424 self.executor_kw['chdir'] = chdir 425 self.is_explicit = is_explicit 426 427 if src_builder is None: 428 src_builder = [] 429 elif not SCons.Util.is_List(src_builder): 430 src_builder = [ src_builder ] 431 self.src_builder = src_builder 432 433 def __bool__(self): 434 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") 435 436 def get_name(self, env): 437 """Attempts to get the name of the Builder. 438 439 Look at the BUILDERS variable of env, expecting it to be a 440 dictionary containing this Builder, and return the key of the 441 dictionary. If there's no key, then return a directly-configured 442 name (if there is one) or the name of the class (by default).""" 443 444 try: 445 index = list(env['BUILDERS'].values()).index(self) 446 return list(env['BUILDERS'].keys())[index] 447 except (AttributeError, KeyError, TypeError, ValueError): 448 try: 449 return self.name 450 except AttributeError: 451 return str(self.__class__) 452 453 def __eq__(self, other): 454 return self.__dict__ == other.__dict__ 455 456 def splitext(self, path, env=None): 457 if not env: 458 env = self.env 459 if env: 460 suffixes = self.src_suffixes(env) 461 else: 462 suffixes = [] 463 return match_splitext(path, suffixes) 464 465 def _adjustixes(self, files, pre, suf, ensure_suffix=False): 466 if not files: 467 return [] 468 result = [] 469 if not SCons.Util.is_List(files): 470 files = [files] 471 472 for f in files: 473 if SCons.Util.is_String(f): 474 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) 475 result.append(f) 476 return result 477 478 def _create_nodes(self, env, target = None, source = None): 479 """Create and return lists of target and source nodes. 480 """ 481 src_suf = self.get_src_suffix(env) 482 483 target_factory = env.get_factory(self.target_factory) 484 source_factory = env.get_factory(self.source_factory) 485 486 source = self._adjustixes(source, None, src_suf) 487 slist = env.arg2nodes(source, source_factory) 488 489 pre = self.get_prefix(env, slist) 490 suf = self.get_suffix(env, slist) 491 492 if target is None: 493 try: 494 t_from_s = slist[0].target_from_source 495 except AttributeError: 496 raise UserError("Do not know how to create a target from source `%s'" % slist[0]) 497 except IndexError: 498 tlist = [] 499 else: 500 splitext = lambda S: self.splitext(S,env) 501 tlist = [ t_from_s(pre, suf, splitext) ] 502 else: 503 # orig_target = target 504 target = self._adjustixes(target, pre, suf, self.ensure_suffix) 505 tlist = env.arg2nodes(target, target_factory, target=target, source=source) 506 507 if self.emitter: 508 # The emitter is going to do str(node), but because we're 509 # being called *from* a builder invocation, the new targets 510 # don't yet have a builder set on them and will look like 511 # source files. Fool the emitter's str() calls by setting 512 # up a temporary builder on the new targets. 513 new_targets = [] 514 for t in tlist: 515 if not t.is_derived(): 516 t.builder_set(self) 517 new_targets.append(t) 518 519 orig_tlist = tlist[:] 520 orig_slist = slist[:] 521 522 target, source = self.emitter(target=tlist, source=slist, env=env) 523 524 # Now delete the temporary builders that we attached to any 525 # new targets, so that _node_errors() doesn't do weird stuff 526 # to them because it thinks they already have builders. 527 for t in new_targets: 528 if t.builder is self: 529 # Only delete the temporary builder if the emitter 530 # didn't change it on us. 531 t.builder_set(None) 532 533 # Have to call arg2nodes yet again, since it is legal for 534 # emitters to spit out strings as well as Node instances. 535 tlist = env.arg2nodes(target, target_factory, 536 target=orig_tlist, source=orig_slist) 537 slist = env.arg2nodes(source, source_factory, 538 target=orig_tlist, source=orig_slist) 539 540 return tlist, slist 541 542 def _execute(self, env, target, source, overwarn={}, executor_kw={}): 543 # We now assume that target and source are lists or None. 544 if self.src_builder: 545 source = self.src_builder_sources(env, source, overwarn) 546 547 if self.single_source and len(source) > 1 and target is None: 548 result = [] 549 if target is None: target = [None]*len(source) 550 for tgt, src in zip(target, source): 551 if tgt is not None: 552 tgt = [tgt] 553 if src is not None: 554 src = [src] 555 result.extend(self._execute(env, tgt, src, overwarn)) 556 return SCons.Node.NodeList(result) 557 558 overwarn.warn() 559 560 tlist, slist = self._create_nodes(env, target, source) 561 562 # If there is more than one target ensure that if we need to reset 563 # the implicit list to new scan of dependency all targets implicit lists 564 # are cleared. (SCons GH Issue #2811 and MongoDB SERVER-33111) 565 if len(tlist) > 1: 566 for t in tlist: 567 t.target_peers = tlist 568 569 # Check for errors with the specified target/source lists. 570 _node_errors(self, env, tlist, slist) 571 572 # The targets are fine, so find or make the appropriate Executor to 573 # build this particular list of targets from this particular list of 574 # sources. 575 576 executor = None 577 key = None 578 579 if self.multi: 580 try: 581 executor = tlist[0].get_executor(create = 0) 582 except (AttributeError, IndexError): 583 pass 584 else: 585 executor.add_sources(slist) 586 587 if executor is None: 588 if not self.action: 589 fmt = "Builder %s must have an action to build %s." 590 raise UserError(fmt % (self.get_name(env or self.env), 591 list(map(str,tlist)))) 592 key = self.action.batch_key(env or self.env, tlist, slist) 593 if key: 594 try: 595 executor = SCons.Executor.GetBatchExecutor(key) 596 except KeyError: 597 pass 598 else: 599 executor.add_batch(tlist, slist) 600 601 if executor is None: 602 executor = SCons.Executor.Executor(self.action, env, [], 603 tlist, slist, executor_kw) 604 if key: 605 SCons.Executor.AddBatchExecutor(key, executor) 606 607 # Now set up the relevant information in the target Nodes themselves. 608 for t in tlist: 609 t.cwd = env.fs.getcwd() 610 t.builder_set(self) 611 t.env_set(env) 612 t.add_source(slist) 613 t.set_executor(executor) 614 t.set_explicit(self.is_explicit) 615 616 if env.get("SCONF_NODE"): 617 for node in tlist: 618 node.attributes.conftest_node = 1 619 620 return SCons.Node.NodeList(tlist) 621 622 def __call__(self, env, target=None, source=None, chdir=_null, **kw): 623 # We now assume that target and source are lists or None. 624 # The caller (typically Environment.BuilderWrapper) is 625 # responsible for converting any scalar values to lists. 626 if chdir is _null: 627 ekw = self.executor_kw 628 else: 629 ekw = self.executor_kw.copy() 630 ekw['chdir'] = chdir 631 if 'chdir' in ekw and SCons.Util.is_String(ekw['chdir']): 632 ekw['chdir'] = env.subst(ekw['chdir']) 633 if kw: 634 if 'srcdir' in kw: 635 def prependDirIfRelative(f, srcdir=kw['srcdir']): 636 import os.path 637 if SCons.Util.is_String(f) and not os.path.isabs(f): 638 f = os.path.join(srcdir, f) 639 return f 640 if not SCons.Util.is_List(source): 641 source = [source] 642 source = list(map(prependDirIfRelative, source)) 643 del kw['srcdir'] 644 if self.overrides: 645 env_kw = self.overrides.copy() 646 env_kw.update(kw) 647 else: 648 env_kw = kw 649 else: 650 env_kw = self.overrides 651 652 # TODO if env_kw: then the following line. there's no purpose in calling if no 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 863class 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 883def 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