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 |