| OLD | NEW |
| 1 """SCons.Builder | 1 """SCons.Builder |
| 2 | 2 |
| 3 Builder object subsystem. | 3 Builder object subsystem. |
| 4 | 4 |
| 5 A Builder object is a callable that encapsulates information about how | 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 | 6 to execute actions to create a target Node (file) from source Nodes |
| 7 (files), and how to create those dependencies for tracking. | 7 (files), and how to create those dependencies for tracking. |
| 8 | 8 |
| 9 The main entry point here is the Builder() factory method. This provides | 9 The main entry point here is the Builder() factory method. This provides |
| 10 a procedural interface that creates the right underlying Builder object | 10 a procedural interface that creates the right underlying Builder object |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 get_suffix() | 69 get_suffix() |
| 70 get_src_suffix() | 70 get_src_suffix() |
| 71 set_src_suffix() | 71 set_src_suffix() |
| 72 Miscellaneous stuff for handling the prefix and suffix | 72 Miscellaneous stuff for handling the prefix and suffix |
| 73 manipulation we use in turning source file names into target | 73 manipulation we use in turning source file names into target |
| 74 file names. | 74 file names. |
| 75 | 75 |
| 76 """ | 76 """ |
| 77 | 77 |
| 78 # | 78 # |
| 79 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundat
ion | 79 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons F
oundation |
| 80 # | 80 # |
| 81 # Permission is hereby granted, free of charge, to any person obtaining | 81 # Permission is hereby granted, free of charge, to any person obtaining |
| 82 # a copy of this software and associated documentation files (the | 82 # a copy of this software and associated documentation files (the |
| 83 # "Software"), to deal in the Software without restriction, including | 83 # "Software"), to deal in the Software without restriction, including |
| 84 # without limitation the rights to use, copy, modify, merge, publish, | 84 # without limitation the rights to use, copy, modify, merge, publish, |
| 85 # distribute, sublicense, and/or sell copies of the Software, and to | 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 | 86 # permit persons to whom the Software is furnished to do so, subject to |
| 87 # the following conditions: | 87 # the following conditions: |
| 88 # | 88 # |
| 89 # The above copyright notice and this permission notice shall be included | 89 # The above copyright notice and this permission notice shall be included |
| 90 # in all copies or substantial portions of the Software. | 90 # in all copies or substantial portions of the Software. |
| 91 # | 91 # |
| 92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | 92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | 93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 95 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 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 | 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 | 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. | 98 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 99 # | 99 # |
| 100 | 100 |
| 101 __revision__ = "src/engine/SCons/Builder.py 3842 2008/12/20 22:59:52 scons" | 101 __revision__ = "src/engine/SCons/Builder.py 3897 2009/01/13 06:45:54 scons" |
| 102 | 102 |
| 103 import UserDict | 103 import UserDict |
| 104 import UserList | 104 import UserList |
| 105 | 105 |
| 106 import SCons.Action | 106 import SCons.Action |
| 107 from SCons.Debug import logInstanceCreation | 107 from SCons.Debug import logInstanceCreation |
| 108 from SCons.Errors import InternalError, UserError | 108 from SCons.Errors import InternalError, UserError |
| 109 import SCons.Executor | 109 import SCons.Executor |
| 110 import SCons.Memoize | 110 import SCons.Memoize |
| 111 import SCons.Node | 111 import SCons.Node |
| 112 import SCons.Node.FS | 112 import SCons.Node.FS |
| 113 import SCons.Util | 113 import SCons.Util |
| 114 import SCons.Warnings | 114 import SCons.Warnings |
| 115 | 115 |
| 116 class _Null: | 116 class _Null: |
| 117 pass | 117 pass |
| 118 | 118 |
| 119 _null = _Null | 119 _null = _Null |
| 120 | 120 |
| 121 def match_splitext(path, suffixes = []): |
| 122 if suffixes: |
| 123 matchsuf = filter(lambda S,path=path: path[-len(S):] == S, |
| 124 suffixes) |
| 125 if matchsuf: |
| 126 suf = max(map(None, map(len, matchsuf), matchsuf))[1] |
| 127 return [path[:-len(suf)], path[-len(suf):]] |
| 128 return SCons.Util.splitext(path) |
| 129 |
| 121 class DictCmdGenerator(SCons.Util.Selector): | 130 class DictCmdGenerator(SCons.Util.Selector): |
| 122 """This is a callable class that can be used as a | 131 """This is a callable class that can be used as a |
| 123 command generator function. It holds on to a dictionary | 132 command generator function. It holds on to a dictionary |
| 124 mapping file suffixes to Actions. It uses that dictionary | 133 mapping file suffixes to Actions. It uses that dictionary |
| 125 to return the proper action based on the file suffix of | 134 to return the proper action based on the file suffix of |
| 126 the source file.""" | 135 the source file.""" |
| 127 | 136 |
| 128 def __init__(self, dict=None, source_ext_match=1): | 137 def __init__(self, dict=None, source_ext_match=1): |
| 129 SCons.Util.Selector.__init__(self, dict) | 138 SCons.Util.Selector.__init__(self, dict) |
| 130 self.source_ext_match = source_ext_match | 139 self.source_ext_match = source_ext_match |
| 131 | 140 |
| 132 def src_suffixes(self): | 141 def src_suffixes(self): |
| 133 return self.keys() | 142 return self.keys() |
| 134 | 143 |
| 135 def add_action(self, suffix, action): | 144 def add_action(self, suffix, action): |
| 136 """Add a suffix-action pair to the mapping. | 145 """Add a suffix-action pair to the mapping. |
| 137 """ | 146 """ |
| 138 self[suffix] = action | 147 self[suffix] = action |
| 139 | 148 |
| 140 def __call__(self, target, source, env, for_signature): | 149 def __call__(self, target, source, env, for_signature): |
| 141 if not source: | 150 if not source: |
| 142 return [] | 151 return [] |
| 143 | 152 |
| 144 if self.source_ext_match: | 153 if self.source_ext_match: |
| 154 suffixes = self.src_suffixes() |
| 145 ext = None | 155 ext = None |
| 146 for src in map(str, source): | 156 for src in map(str, source): |
| 147 my_ext = SCons.Util.splitext(src)[1] | 157 my_ext = match_splitext(src, suffixes)[1] |
| 148 if ext and my_ext != ext: | 158 if ext and my_ext != ext: |
| 149 raise UserError("While building `%s' from `%s': Cannot build
multiple sources with different extensions: %s, %s" % (repr(map(str, target)),
src, ext, my_ext)) | 159 raise UserError("While building `%s' from `%s': Cannot build
multiple sources with different extensions: %s, %s" % (repr(map(str, target)),
src, ext, my_ext)) |
| 150 ext = my_ext | 160 ext = my_ext |
| 151 else: | 161 else: |
| 152 ext = SCons.Util.splitext(str(source[0]))[1] | 162 ext = match_splitext(str(source[0]), self.src_suffixes())[1] |
| 153 | 163 |
| 154 if not ext: | 164 if not ext: |
| 165 #return ext |
| 155 raise UserError("While building `%s': Cannot deduce file extension f
rom source files: %s" % (repr(map(str, target)), repr(map(str, source)))) | 166 raise UserError("While building `%s': Cannot deduce file extension f
rom source files: %s" % (repr(map(str, target)), repr(map(str, source)))) |
| 156 | 167 |
| 157 try: | 168 try: |
| 158 ret = SCons.Util.Selector.__call__(self, env, source) | 169 ret = SCons.Util.Selector.__call__(self, env, source, ext) |
| 159 except KeyError, e: | 170 except KeyError, e: |
| 160 raise UserError("Ambiguous suffixes after environment substitution:
%s == %s == %s" % (e[0], e[1], e[2])) | 171 raise UserError("Ambiguous suffixes after environment substitution:
%s == %s == %s" % (e[0], e[1], e[2])) |
| 161 if ret is None: | 172 if ret is None: |
| 162 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." %
\ | 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." %
\ |
| 163 (repr(map(str, target)), repr(map(str, source)), ext
, repr(self.keys()))) | 174 (repr(map(str, target)), repr(map(str, source)), ext
, repr(self.keys()))) |
| 164 return ret | 175 return ret |
| 165 | 176 |
| 166 class CallableSelector(SCons.Util.Selector): | 177 class CallableSelector(SCons.Util.Selector): |
| 167 """A callable dictionary that will, in turn, call the value it | 178 """A callable dictionary that will, in turn, call the value it |
| 168 finds if it can.""" | 179 finds if it can.""" |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 if t_contents == contents: | 299 if t_contents == contents: |
| 289 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)) | 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)) |
| 290 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarni
ng, msg) | 301 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarni
ng, msg) |
| 291 else: | 302 else: |
| 292 msg = "Two environments with different actions were specifie
d for the same target: %s" % t | 303 msg = "Two environments with different actions were specifie
d for the same target: %s" % t |
| 293 raise UserError, msg | 304 raise UserError, msg |
| 294 if builder.multi: | 305 if builder.multi: |
| 295 if t.builder != builder: | 306 if t.builder != builder: |
| 296 msg = "Two different builders (%s and %s) were specified for
the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) | 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) |
| 297 raise UserError, msg | 308 raise UserError, msg |
| 298 if t.get_executor().targets != tlist: | 309 # TODO(batch): list constructed each time! |
| 299 msg = "Two different target lists have a target in common: %
s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tli
st)) | 310 if t.get_executor().get_all_targets() != tlist: |
| 311 msg = "Two different target lists have a target in common: %
s (from %s and from %s)" % (t, map(str, t.get_executor().get_all_targets()), ma
p(str, tlist)) |
| 300 raise UserError, msg | 312 raise UserError, msg |
| 301 elif t.sources != slist: | 313 elif t.sources != slist: |
| 302 msg = "Multiple ways to build the same target were specified for
: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist)) | 314 msg = "Multiple ways to build the same target were specified for
: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist)) |
| 303 raise UserError, msg | 315 raise UserError, msg |
| 304 | 316 |
| 305 if builder.single_source: | 317 if builder.single_source: |
| 306 if len(slist) > 1: | 318 if len(slist) > 1: |
| 307 raise UserError, "More than one source given for single-source build
er: targets=%s sources=%s" % (map(str,tlist), map(str,slist)) | 319 raise UserError, "More than one source given for single-source build
er: targets=%s sources=%s" % (map(str,tlist), map(str,slist)) |
| 308 | 320 |
| 309 class EmitterProxy: | 321 class EmitterProxy: |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 434 except AttributeError: | 446 except AttributeError: |
| 435 return str(self.__class__) | 447 return str(self.__class__) |
| 436 | 448 |
| 437 def __cmp__(self, other): | 449 def __cmp__(self, other): |
| 438 return cmp(self.__dict__, other.__dict__) | 450 return cmp(self.__dict__, other.__dict__) |
| 439 | 451 |
| 440 def splitext(self, path, env=None): | 452 def splitext(self, path, env=None): |
| 441 if not env: | 453 if not env: |
| 442 env = self.env | 454 env = self.env |
| 443 if env: | 455 if env: |
| 444 matchsuf = filter(lambda S,path=path: path[-len(S):] == S, | 456 suffixes = self.src_suffixes(env) |
| 445 self.src_suffixes(env)) | |
| 446 if matchsuf: | |
| 447 suf = max(map(None, map(len, matchsuf), matchsuf))[1] | |
| 448 return [path[:-len(suf)], path[-len(suf):]] | |
| 449 return SCons.Util.splitext(path) | |
| 450 | |
| 451 def get_single_executor(self, env, tlist, slist, executor_kw): | |
| 452 if not self.action: | |
| 453 raise UserError, "Builder %s must have an action to build %s."%(self
.get_name(env or self.env), map(str,tlist)) | |
| 454 return self.action.get_executor(env or self.env, | |
| 455 [], # env already has overrides | |
| 456 tlist, | |
| 457 slist, | |
| 458 executor_kw) | |
| 459 | |
| 460 def get_multi_executor(self, env, tlist, slist, executor_kw): | |
| 461 try: | |
| 462 executor = tlist[0].get_executor(create = 0) | |
| 463 except (AttributeError, IndexError): | |
| 464 return self.get_single_executor(env, tlist, slist, executor_kw) | |
| 465 else: | 457 else: |
| 466 executor.add_sources(slist) | 458 suffixes = [] |
| 467 return executor | 459 return match_splitext(path, suffixes) |
| 468 | 460 |
| 469 def _adjustixes(self, files, pre, suf, ensure_suffix=False): | 461 def _adjustixes(self, files, pre, suf, ensure_suffix=False): |
| 470 if not files: | 462 if not files: |
| 471 return [] | 463 return [] |
| 472 result = [] | 464 result = [] |
| 473 if not SCons.Util.is_List(files): | 465 if not SCons.Util.is_List(files): |
| 474 files = [files] | 466 files = [files] |
| 475 | 467 |
| 476 for f in files: | 468 for f in files: |
| 477 if SCons.Util.is_String(f): | 469 if SCons.Util.is_String(f): |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 overwarn.warn() | 551 overwarn.warn() |
| 560 | 552 |
| 561 tlist, slist = self._create_nodes(env, target, source) | 553 tlist, slist = self._create_nodes(env, target, source) |
| 562 | 554 |
| 563 # Check for errors with the specified target/source lists. | 555 # Check for errors with the specified target/source lists. |
| 564 _node_errors(self, env, tlist, slist) | 556 _node_errors(self, env, tlist, slist) |
| 565 | 557 |
| 566 # The targets are fine, so find or make the appropriate Executor to | 558 # The targets are fine, so find or make the appropriate Executor to |
| 567 # build this particular list of targets from this particular list of | 559 # build this particular list of targets from this particular list of |
| 568 # sources. | 560 # sources. |
| 561 |
| 562 executor = None |
| 563 key = None |
| 564 |
| 569 if self.multi: | 565 if self.multi: |
| 570 get_executor = self.get_multi_executor | 566 try: |
| 571 else: | 567 executor = tlist[0].get_executor(create = 0) |
| 572 get_executor = self.get_single_executor | 568 except (AttributeError, IndexError): |
| 573 executor = get_executor(env, tlist, slist, executor_kw) | 569 pass |
| 570 else: |
| 571 executor.add_sources(slist) |
| 572 |
| 573 if executor is None: |
| 574 if not self.action: |
| 575 fmt = "Builder %s must have an action to build %s." |
| 576 raise UserError, fmt % (self.get_name(env or self.env), |
| 577 map(str,tlist)) |
| 578 key = self.action.batch_key(env or self.env, tlist, slist) |
| 579 if key: |
| 580 try: |
| 581 executor = SCons.Executor.GetBatchExecutor(key) |
| 582 except KeyError: |
| 583 pass |
| 584 else: |
| 585 executor.add_batch(tlist, slist) |
| 586 |
| 587 if executor is None: |
| 588 executor = SCons.Executor.Executor(self.action, env, [], |
| 589 tlist, slist, executor_kw) |
| 590 if key: |
| 591 SCons.Executor.AddBatchExecutor(key, executor) |
| 574 | 592 |
| 575 # Now set up the relevant information in the target Nodes themselves. | 593 # Now set up the relevant information in the target Nodes themselves. |
| 576 for t in tlist: | 594 for t in tlist: |
| 577 t.cwd = env.fs.getcwd() | 595 t.cwd = env.fs.getcwd() |
| 578 t.builder_set(self) | 596 t.builder_set(self) |
| 579 t.env_set(env) | 597 t.env_set(env) |
| 580 t.add_source(slist) | 598 t.add_source(slist) |
| 581 t.set_executor(executor) | 599 t.set_executor(executor) |
| 582 t.set_explicit(self.is_explicit) | 600 t.set_explicit(self.is_explicit) |
| 583 | 601 |
| (...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 835 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') | 853 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') |
| 836 SCons.Util.Proxy.__init__(self, builder) | 854 SCons.Util.Proxy.__init__(self, builder) |
| 837 | 855 |
| 838 # cmdgen should always be an instance of DictCmdGenerator. | 856 # cmdgen should always be an instance of DictCmdGenerator. |
| 839 self.cmdgen = cmdgen | 857 self.cmdgen = cmdgen |
| 840 self.builder = builder | 858 self.builder = builder |
| 841 | 859 |
| 842 def add_action(self, suffix, action): | 860 def add_action(self, suffix, action): |
| 843 self.cmdgen.add_action(suffix, action) | 861 self.cmdgen.add_action(suffix, action) |
| 844 self.set_src_suffix(self.cmdgen.src_suffixes()) | 862 self.set_src_suffix(self.cmdgen.src_suffixes()) |
| OLD | NEW |