OLD | NEW |
(Empty) | |
| 1 """SCons.Builder |
| 2 |
| 3 Builder object subsystem. |
| 4 |
| 5 A Builder object is a callable that encapsulates information about how |
| 6 to execute actions to create a target Node (file) from source Nodes |
| 7 (files), and how to create those dependencies for tracking. |
| 8 |
| 9 The main entry point here is the Builder() factory method. This provides |
| 10 a procedural interface that creates the right underlying Builder object |
| 11 based on the keyword arguments supplied and the types of the arguments. |
| 12 |
| 13 The goal is for this external interface to be simple enough that the |
| 14 vast majority of users can create new Builders as necessary to support |
| 15 building new types of files in their configurations, without having to |
| 16 dive any deeper into this subsystem. |
| 17 |
| 18 The base class here is BuilderBase. This is a concrete base class which |
| 19 does, in fact, represent the Builder objects that we (or users) create. |
| 20 |
| 21 There is also a proxy that looks like a Builder: |
| 22 |
| 23 CompositeBuilder |
| 24 |
| 25 This proxies for a Builder with an action that is actually a |
| 26 dictionary that knows how to map file suffixes to a specific |
| 27 action. This is so that we can invoke different actions |
| 28 (compilers, compile options) for different flavors of source |
| 29 files. |
| 30 |
| 31 Builders and their proxies have the following public interface methods |
| 32 used by other modules: |
| 33 |
| 34 __call__() |
| 35 THE public interface. Calling a Builder object (with the |
| 36 use of internal helper methods) sets up the target and source |
| 37 dependencies, appropriate mapping to a specific action, and the |
| 38 environment manipulation necessary for overridden construction |
| 39 variable. This also takes care of warning about possible mistakes |
| 40 in keyword arguments. |
| 41 |
| 42 add_emitter() |
| 43 Adds an emitter for a specific file suffix, used by some Tool |
| 44 modules to specify that (for example) a yacc invocation on a .y |
| 45 can create a .h *and* a .c file. |
| 46 |
| 47 add_action() |
| 48 Adds an action for a specific file suffix, heavily used by |
| 49 Tool modules to add their specific action(s) for turning |
| 50 a source file into an object file to the global static |
| 51 and shared object file Builders. |
| 52 |
| 53 There are the following methods for internal use within this module: |
| 54 |
| 55 _execute() |
| 56 The internal method that handles the heavily lifting when a |
| 57 Builder is called. This is used so that the __call__() methods |
| 58 can set up warning about possible mistakes in keyword-argument |
| 59 overrides, and *then* execute all of the steps necessary so that |
| 60 the warnings only occur once. |
| 61 |
| 62 get_name() |
| 63 Returns the Builder's name within a specific Environment, |
| 64 primarily used to try to return helpful information in error |
| 65 messages. |
| 66 |
| 67 adjust_suffix() |
| 68 get_prefix() |
| 69 get_suffix() |
| 70 get_src_suffix() |
| 71 set_src_suffix() |
| 72 Miscellaneous stuff for handling the prefix and suffix |
| 73 manipulation we use in turning source file names into target |
| 74 file names. |
| 75 |
| 76 """ |
| 77 |
| 78 # |
| 79 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 80 # |
| 81 # Permission is hereby granted, free of charge, to any person obtaining |
| 82 # a copy of this software and associated documentation files (the |
| 83 # "Software"), to deal in the Software without restriction, including |
| 84 # without limitation the rights to use, copy, modify, merge, publish, |
| 85 # distribute, sublicense, and/or sell copies of the Software, and to |
| 86 # permit persons to whom the Software is furnished to do so, subject to |
| 87 # the following conditions: |
| 88 # |
| 89 # The above copyright notice and this permission notice shall be included |
| 90 # in all copies or substantial portions of the Software. |
| 91 # |
| 92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 95 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 96 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 97 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 98 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 99 |
| 100 __revision__ = "src/engine/SCons/Builder.py 5134 2010/08/16 23:02:40 bdeegan" |
| 101 |
| 102 import collections |
| 103 |
| 104 import SCons.Action |
| 105 from SCons.Debug import logInstanceCreation |
| 106 from SCons.Errors import InternalError, UserError |
| 107 import SCons.Executor |
| 108 import SCons.Memoize |
| 109 import SCons.Node |
| 110 import SCons.Node.FS |
| 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, 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 bu
ild from a source file with suffix `%s'. Expected a suffix in this list: %s." %
\ |
| 174 (repr(list(map(str, target))), repr(list(map(str, so
urce))), 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 __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') |
| 229 self.already_warned = None |
| 230 def warn(self): |
| 231 if self.already_warned: |
| 232 return |
| 233 for k in self.keys(): |
| 234 if 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, ms
g) |
| 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 specifi
ed for: %s" % t) |
| 293 if t.has_explicit_builder(): |
| 294 if not t.env is None and not t.env is env: |
| 295 action = t.builder.action |
| 296 t_contents = action.get_contents(tlist, slist, t.env) |
| 297 contents = action.get_contents(tlist, slist, env) |
| 298 |
| 299 if t_contents == contents: |
| 300 msg = "Two different environments were specified for target
%s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist
, slist, t.env)) |
| 301 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarni
ng, msg) |
| 302 else: |
| 303 msg = "Two environments with different actions were specifie
d for the same target: %s" % t |
| 304 raise UserError(msg) |
| 305 if builder.multi: |
| 306 if t.builder != builder: |
| 307 msg = "Two different builders (%s and %s) were specified for
the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) |
| 308 raise UserError(msg) |
| 309 # TODO(batch): list constructed each time! |
| 310 if t.get_executor().get_all_targets() != tlist: |
| 311 msg = "Two different target lists have a target in common: %
s (from %s and from %s)" % (t, 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, slis
t))) |
| 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 builde
r: 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 __cmp__(self, other): |
| 349 return cmp(self.var, other.var) |
| 350 |
| 351 class BuilderBase(object): |
| 352 """Base class for Builders, objects that create output |
| 353 nodes (files) from input nodes (files). |
| 354 """ |
| 355 |
| 356 if SCons.Memoize.use_memoizer: |
| 357 __metaclass__ = SCons.Memoize.Memoized_Metaclass |
| 358 |
| 359 memoizer_counters = [] |
| 360 |
| 361 def __init__(self, action = None, |
| 362 prefix = '', |
| 363 suffix = '', |
| 364 src_suffix = '', |
| 365 target_factory = None, |
| 366 source_factory = None, |
| 367 target_scanner = None, |
| 368 source_scanner = None, |
| 369 emitter = None, |
| 370 multi = 0, |
| 371 env = None, |
| 372 single_source = 0, |
| 373 name = None, |
| 374 chdir = _null, |
| 375 is_explicit = 1, |
| 376 src_builder = None, |
| 377 ensure_suffix = False, |
| 378 **overrides): |
| 379 if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') |
| 380 self._memo = {} |
| 381 self.action = action |
| 382 self.multi = multi |
| 383 if SCons.Util.is_Dict(prefix): |
| 384 prefix = CallableSelector(prefix) |
| 385 self.prefix = prefix |
| 386 if SCons.Util.is_Dict(suffix): |
| 387 suffix = CallableSelector(suffix) |
| 388 self.env = env |
| 389 self.single_source = single_source |
| 390 if 'overrides' in overrides: |
| 391 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, |
| 392 "The \"overrides\" keyword to Builder() creation has been deprec
ated;\n" +\ |
| 393 "\tspecify the items as keyword arguments to the Builder() call
instead.") |
| 394 overrides.update(overrides['overrides']) |
| 395 del overrides['overrides'] |
| 396 if 'scanner' in overrides: |
| 397 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, |
| 398 "The \"scanner\" keyword to Builder() creation h
as been deprecated;\n" |
| 399 "\tuse: source_scanner or target_scanner as appr
opriate.") |
| 400 del overrides['scanner'] |
| 401 self.overrides = overrides |
| 402 |
| 403 self.set_suffix(suffix) |
| 404 self.set_src_suffix(src_suffix) |
| 405 self.ensure_suffix = ensure_suffix |
| 406 |
| 407 self.target_factory = target_factory |
| 408 self.source_factory = source_factory |
| 409 self.target_scanner = target_scanner |
| 410 self.source_scanner = source_scanner |
| 411 |
| 412 self.emitter = emitter |
| 413 |
| 414 # Optional Builder name should only be used for Builders |
| 415 # that don't get attached to construction environments. |
| 416 if name: |
| 417 self.name = name |
| 418 self.executor_kw = {} |
| 419 if not chdir is _null: |
| 420 self.executor_kw['chdir'] = chdir |
| 421 self.is_explicit = is_explicit |
| 422 |
| 423 if src_builder is None: |
| 424 src_builder = [] |
| 425 elif not SCons.Util.is_List(src_builder): |
| 426 src_builder = [ src_builder ] |
| 427 self.src_builder = src_builder |
| 428 |
| 429 def __nonzero__(self): |
| 430 raise InternalError("Do not test for the Node.builder attribute directly
; use Node.has_builder() instead") |
| 431 |
| 432 def get_name(self, env): |
| 433 """Attempts to get the name of the Builder. |
| 434 |
| 435 Look at the BUILDERS variable of env, expecting it to be a |
| 436 dictionary containing this Builder, and return the key of the |
| 437 dictionary. If there's no key, then return a directly-configured |
| 438 name (if there is one) or the name of the class (by default).""" |
| 439 |
| 440 try: |
| 441 index = list(env['BUILDERS'].values()).index(self) |
| 442 return list(env['BUILDERS'].keys())[index] |
| 443 except (AttributeError, KeyError, TypeError, ValueError): |
| 444 try: |
| 445 return self.name |
| 446 except AttributeError: |
| 447 return str(self.__class__) |
| 448 |
| 449 def __cmp__(self, other): |
| 450 return cmp(self.__dict__, other.__dict__) |
| 451 |
| 452 def splitext(self, path, env=None): |
| 453 if not env: |
| 454 env = self.env |
| 455 if env: |
| 456 suffixes = self.src_suffixes(env) |
| 457 else: |
| 458 suffixes = [] |
| 459 return match_splitext(path, suffixes) |
| 460 |
| 461 def _adjustixes(self, files, pre, suf, ensure_suffix=False): |
| 462 if not files: |
| 463 return [] |
| 464 result = [] |
| 465 if not SCons.Util.is_List(files): |
| 466 files = [files] |
| 467 |
| 468 for f in files: |
| 469 if SCons.Util.is_String(f): |
| 470 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) |
| 471 result.append(f) |
| 472 return result |
| 473 |
| 474 def _create_nodes(self, env, target = None, source = None): |
| 475 """Create and return lists of target and source nodes. |
| 476 """ |
| 477 src_suf = self.get_src_suffix(env) |
| 478 |
| 479 target_factory = env.get_factory(self.target_factory) |
| 480 source_factory = env.get_factory(self.source_factory) |
| 481 |
| 482 source = self._adjustixes(source, None, src_suf) |
| 483 slist = env.arg2nodes(source, source_factory) |
| 484 |
| 485 pre = self.get_prefix(env, slist) |
| 486 suf = self.get_suffix(env, slist) |
| 487 |
| 488 if target is None: |
| 489 try: |
| 490 t_from_s = slist[0].target_from_source |
| 491 except AttributeError: |
| 492 raise UserError("Do not know how to create a target from source
`%s'" % slist[0]) |
| 493 except IndexError: |
| 494 tlist = [] |
| 495 else: |
| 496 splitext = lambda S: self.splitext(S,env) |
| 497 tlist = [ t_from_s(pre, suf, splitext) ] |
| 498 else: |
| 499 target = self._adjustixes(target, pre, suf, self.ensure_suffix) |
| 500 tlist = env.arg2nodes(target, target_factory, target=target, source=
source) |
| 501 |
| 502 if self.emitter: |
| 503 # The emitter is going to do str(node), but because we're |
| 504 # being called *from* a builder invocation, the new targets |
| 505 # don't yet have a builder set on them and will look like |
| 506 # source files. Fool the emitter's str() calls by setting |
| 507 # up a temporary builder on the new targets. |
| 508 new_targets = [] |
| 509 for t in tlist: |
| 510 if not t.is_derived(): |
| 511 t.builder_set(self) |
| 512 new_targets.append(t) |
| 513 |
| 514 orig_tlist = tlist[:] |
| 515 orig_slist = slist[:] |
| 516 |
| 517 target, source = self.emitter(target=tlist, source=slist, env=env) |
| 518 |
| 519 # Now delete the temporary builders that we attached to any |
| 520 # new targets, so that _node_errors() doesn't do weird stuff |
| 521 # to them because it thinks they already have builders. |
| 522 for t in new_targets: |
| 523 if t.builder is self: |
| 524 # Only delete the temporary builder if the emitter |
| 525 # didn't change it on us. |
| 526 t.builder_set(None) |
| 527 |
| 528 # Have to call arg2nodes yet again, since it is legal for |
| 529 # emitters to spit out strings as well as Node instances. |
| 530 tlist = env.arg2nodes(target, target_factory, |
| 531 target=orig_tlist, source=orig_slist) |
| 532 slist = env.arg2nodes(source, source_factory, |
| 533 target=orig_tlist, source=orig_slist) |
| 534 |
| 535 return tlist, slist |
| 536 |
| 537 def _execute(self, env, target, source, overwarn={}, executor_kw={}): |
| 538 # We now assume that target and source are lists or None. |
| 539 if self.src_builder: |
| 540 source = self.src_builder_sources(env, source, overwarn) |
| 541 |
| 542 if self.single_source and len(source) > 1 and target is None: |
| 543 result = [] |
| 544 if target is None: target = [None]*len(source) |
| 545 for tgt, src in zip(target, source): |
| 546 if not tgt is None: tgt = [tgt] |
| 547 if not src is None: src = [src] |
| 548 result.extend(self._execute(env, tgt, src, overwarn)) |
| 549 return SCons.Node.NodeList(result) |
| 550 |
| 551 overwarn.warn() |
| 552 |
| 553 tlist, slist = self._create_nodes(env, target, source) |
| 554 |
| 555 # Check for errors with the specified target/source lists. |
| 556 _node_errors(self, env, tlist, slist) |
| 557 |
| 558 # The targets are fine, so find or make the appropriate Executor to |
| 559 # build this particular list of targets from this particular list of |
| 560 # sources. |
| 561 |
| 562 executor = None |
| 563 key = None |
| 564 |
| 565 if self.multi: |
| 566 try: |
| 567 executor = tlist[0].get_executor(create = 0) |
| 568 except (AttributeError, IndexError): |
| 569 pass |
| 570 else: |
| 571 executor.add_sources(slist) |
| 572 |
| 573 if executor is None: |
| 574 if not self.action: |
| 575 fmt = "Builder %s must have an action to build %s." |
| 576 raise UserError(fmt % (self.get_name(env or self.env), |
| 577 list(map(str,tlist)))) |
| 578 key = self.action.batch_key(env or self.env, tlist, slist) |
| 579 if key: |
| 580 try: |
| 581 executor = SCons.Executor.GetBatchExecutor(key) |
| 582 except KeyError: |
| 583 pass |
| 584 else: |
| 585 executor.add_batch(tlist, slist) |
| 586 |
| 587 if executor is None: |
| 588 executor = SCons.Executor.Executor(self.action, env, [], |
| 589 tlist, slist, executor_kw) |
| 590 if key: |
| 591 SCons.Executor.AddBatchExecutor(key, executor) |
| 592 |
| 593 # Now set up the relevant information in the target Nodes themselves. |
| 594 for t in tlist: |
| 595 t.cwd = env.fs.getcwd() |
| 596 t.builder_set(self) |
| 597 t.env_set(env) |
| 598 t.add_source(slist) |
| 599 t.set_executor(executor) |
| 600 t.set_explicit(self.is_explicit) |
| 601 |
| 602 return SCons.Node.NodeList(tlist) |
| 603 |
| 604 def __call__(self, env, target=None, source=None, chdir=_null, **kw): |
| 605 # We now assume that target and source are lists or None. |
| 606 # The caller (typically Environment.BuilderWrapper) is |
| 607 # responsible for converting any scalar values to lists. |
| 608 if chdir is _null: |
| 609 ekw = self.executor_kw |
| 610 else: |
| 611 ekw = self.executor_kw.copy() |
| 612 ekw['chdir'] = chdir |
| 613 if kw: |
| 614 if 'srcdir' in kw: |
| 615 def prependDirIfRelative(f, srcdir=kw['srcdir']): |
| 616 import os.path |
| 617 if SCons.Util.is_String(f) and not os.path.isabs(f): |
| 618 f = os.path.join(srcdir, f) |
| 619 return f |
| 620 if not SCons.Util.is_List(source): |
| 621 source = [source] |
| 622 source = list(map(prependDirIfRelative, source)) |
| 623 del kw['srcdir'] |
| 624 if self.overrides: |
| 625 env_kw = self.overrides.copy() |
| 626 env_kw.update(kw) |
| 627 else: |
| 628 env_kw = kw |
| 629 else: |
| 630 env_kw = self.overrides |
| 631 env = env.Override(env_kw) |
| 632 return self._execute(env, target, source, OverrideWarner(kw), ekw) |
| 633 |
| 634 def adjust_suffix(self, suff): |
| 635 if suff and not suff[0] in [ '.', '_', '$' ]: |
| 636 return '.' + suff |
| 637 return suff |
| 638 |
| 639 def get_prefix(self, env, sources=[]): |
| 640 prefix = self.prefix |
| 641 if callable(prefix): |
| 642 prefix = prefix(env, sources) |
| 643 return env.subst(prefix) |
| 644 |
| 645 def set_suffix(self, suffix): |
| 646 if not callable(suffix): |
| 647 suffix = self.adjust_suffix(suffix) |
| 648 self.suffix = suffix |
| 649 |
| 650 def get_suffix(self, env, sources=[]): |
| 651 suffix = self.suffix |
| 652 if callable(suffix): |
| 653 suffix = suffix(env, sources) |
| 654 return env.subst(suffix) |
| 655 |
| 656 def set_src_suffix(self, src_suffix): |
| 657 if not src_suffix: |
| 658 src_suffix = [] |
| 659 elif not SCons.Util.is_List(src_suffix): |
| 660 src_suffix = [ src_suffix ] |
| 661 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for
suf in src_suffix] |
| 662 |
| 663 def get_src_suffix(self, env): |
| 664 """Get the first src_suffix in the list of src_suffixes.""" |
| 665 ret = self.src_suffixes(env) |
| 666 if not ret: |
| 667 return '' |
| 668 return ret[0] |
| 669 |
| 670 def add_emitter(self, suffix, emitter): |
| 671 """Add a suffix-emitter mapping to this Builder. |
| 672 |
| 673 This assumes that emitter has been initialized with an |
| 674 appropriate dictionary type, and will throw a TypeError if |
| 675 not, so the caller is responsible for knowing that this is an |
| 676 appropriate method to call for the Builder in question. |
| 677 """ |
| 678 self.emitter[suffix] = emitter |
| 679 |
| 680 def add_src_builder(self, builder): |
| 681 """ |
| 682 Add a new Builder to the list of src_builders. |
| 683 |
| 684 This requires wiping out cached values so that the computed |
| 685 lists of source suffixes get re-calculated. |
| 686 """ |
| 687 self._memo = {} |
| 688 self.src_builder.append(builder) |
| 689 |
| 690 def _get_sdict(self, env): |
| 691 """ |
| 692 Returns a dictionary mapping all of the source suffixes of all |
| 693 src_builders of this Builder to the underlying Builder that |
| 694 should be called first. |
| 695 |
| 696 This dictionary is used for each target specified, so we save a |
| 697 lot of extra computation by memoizing it for each construction |
| 698 environment. |
| 699 |
| 700 Note that this is re-computed each time, not cached, because there |
| 701 might be changes to one of our source Builders (or one of their |
| 702 source Builders, and so on, and so on...) that we can't "see." |
| 703 |
| 704 The underlying methods we call cache their computed values, |
| 705 though, so we hope repeatedly aggregating them into a dictionary |
| 706 like this won't be too big a hit. We may need to look for a |
| 707 better way to do this if performance data show this has turned |
| 708 into a significant bottleneck. |
| 709 """ |
| 710 sdict = {} |
| 711 for bld in self.get_src_builders(env): |
| 712 for suf in bld.src_suffixes(env): |
| 713 sdict[suf] = bld |
| 714 return sdict |
| 715 |
| 716 def src_builder_sources(self, env, source, overwarn={}): |
| 717 sdict = self._get_sdict(env) |
| 718 |
| 719 src_suffixes = self.src_suffixes(env) |
| 720 |
| 721 lengths = list(set(map(len, src_suffixes))) |
| 722 |
| 723 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): |
| 724 node_suffixes = [name[-l:] for l in lengths] |
| 725 for suf in src_suffixes: |
| 726 if suf in node_suffixes: |
| 727 return suf |
| 728 return None |
| 729 |
| 730 result = [] |
| 731 for s in SCons.Util.flatten(source): |
| 732 if SCons.Util.is_String(s): |
| 733 match_suffix = match_src_suffix(env.subst(s)) |
| 734 if not match_suffix and not '.' in s: |
| 735 src_suf = self.get_src_suffix(env) |
| 736 s = self._adjustixes(s, None, src_suf)[0] |
| 737 else: |
| 738 match_suffix = match_src_suffix(s.name) |
| 739 if match_suffix: |
| 740 try: |
| 741 bld = sdict[match_suffix] |
| 742 except KeyError: |
| 743 result.append(s) |
| 744 else: |
| 745 tlist = bld._execute(env, None, [s], overwarn) |
| 746 # If the subsidiary Builder returned more than one |
| 747 # target, then filter out any sources that this |
| 748 # Builder isn't capable of building. |
| 749 if len(tlist) > 1: |
| 750 tlist = [t for t in tlist if match_src_suffix(t.name)] |
| 751 result.extend(tlist) |
| 752 else: |
| 753 result.append(s) |
| 754 |
| 755 source_factory = env.get_factory(self.source_factory) |
| 756 |
| 757 return env.arg2nodes(result, source_factory) |
| 758 |
| 759 def _get_src_builders_key(self, env): |
| 760 return id(env) |
| 761 |
| 762 memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_sr
c_builders_key)) |
| 763 |
| 764 def get_src_builders(self, env): |
| 765 """ |
| 766 Returns the list of source Builders for this Builder. |
| 767 |
| 768 This exists mainly to look up Builders referenced as |
| 769 strings in the 'BUILDER' variable of the construction |
| 770 environment and cache the result. |
| 771 """ |
| 772 memo_key = id(env) |
| 773 try: |
| 774 memo_dict = self._memo['get_src_builders'] |
| 775 except KeyError: |
| 776 memo_dict = {} |
| 777 self._memo['get_src_builders'] = memo_dict |
| 778 else: |
| 779 try: |
| 780 return memo_dict[memo_key] |
| 781 except KeyError: |
| 782 pass |
| 783 |
| 784 builders = [] |
| 785 for bld in self.src_builder: |
| 786 if SCons.Util.is_String(bld): |
| 787 try: |
| 788 bld = env['BUILDERS'][bld] |
| 789 except KeyError: |
| 790 continue |
| 791 builders.append(bld) |
| 792 |
| 793 memo_dict[memo_key] = builders |
| 794 return builders |
| 795 |
| 796 def _subst_src_suffixes_key(self, env): |
| 797 return id(env) |
| 798 |
| 799 memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subs
t_src_suffixes_key)) |
| 800 |
| 801 def subst_src_suffixes(self, env): |
| 802 """ |
| 803 The suffix list may contain construction variable expansions, |
| 804 so we have to evaluate the individual strings. To avoid doing |
| 805 this over and over, we memoize the results for each construction |
| 806 environment. |
| 807 """ |
| 808 memo_key = id(env) |
| 809 try: |
| 810 memo_dict = self._memo['subst_src_suffixes'] |
| 811 except KeyError: |
| 812 memo_dict = {} |
| 813 self._memo['subst_src_suffixes'] = memo_dict |
| 814 else: |
| 815 try: |
| 816 return memo_dict[memo_key] |
| 817 except KeyError: |
| 818 pass |
| 819 suffixes = [env.subst(x) for x in self.src_suffix] |
| 820 memo_dict[memo_key] = suffixes |
| 821 return suffixes |
| 822 |
| 823 def src_suffixes(self, env): |
| 824 """ |
| 825 Returns the list of source suffixes for all src_builders of this |
| 826 Builder. |
| 827 |
| 828 This is essentially a recursive descent of the src_builder "tree." |
| 829 (This value isn't cached because there may be changes in a |
| 830 src_builder many levels deep that we can't see.) |
| 831 """ |
| 832 sdict = {} |
| 833 suffixes = self.subst_src_suffixes(env) |
| 834 for s in suffixes: |
| 835 sdict[s] = 1 |
| 836 for builder in self.get_src_builders(env): |
| 837 for s in builder.src_suffixes(env): |
| 838 if s not in sdict: |
| 839 sdict[s] = 1 |
| 840 suffixes.append(s) |
| 841 return suffixes |
| 842 |
| 843 class CompositeBuilder(SCons.Util.Proxy): |
| 844 """A Builder Proxy whose main purpose is to always have |
| 845 a DictCmdGenerator as its action, and to provide access |
| 846 to the DictCmdGenerator's add_action() method. |
| 847 """ |
| 848 |
| 849 def __init__(self, builder, cmdgen): |
| 850 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') |
| 851 SCons.Util.Proxy.__init__(self, builder) |
| 852 |
| 853 # cmdgen should always be an instance of DictCmdGenerator. |
| 854 self.cmdgen = cmdgen |
| 855 self.builder = builder |
| 856 |
| 857 __call__ = SCons.Util.Delegate('__call__') |
| 858 |
| 859 def add_action(self, suffix, action): |
| 860 self.cmdgen.add_action(suffix, action) |
| 861 self.set_src_suffix(self.cmdgen.src_suffixes()) |
| 862 |
| 863 def is_a_Builder(obj): |
| 864 """"Returns True iff the specified obj is one of our Builder classes. |
| 865 |
| 866 The test is complicated a bit by the fact that CompositeBuilder |
| 867 is a proxy, not a subclass of BuilderBase. |
| 868 """ |
| 869 return (isinstance(obj, BuilderBase) |
| 870 or isinstance(obj, CompositeBuilder) |
| 871 or callable(obj)) |
| 872 |
| 873 # Local Variables: |
| 874 # tab-width:4 |
| 875 # indent-tabs-mode:nil |
| 876 # End: |
| 877 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |