OLD | NEW |
1 """SCons.Executor | 1 """SCons.Executor |
2 | 2 |
3 A module for executing actions with specific lists of target and source | 3 A module for executing actions with specific lists of target and source |
4 Nodes. | 4 Nodes. |
5 | 5 |
6 """ | 6 """ |
7 | 7 |
8 # | 8 # |
9 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundat
ion | 9 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons F
oundation |
10 # | 10 # |
11 # Permission is hereby granted, free of charge, to any person obtaining | 11 # Permission is hereby granted, free of charge, to any person obtaining |
12 # a copy of this software and associated documentation files (the | 12 # a copy of this software and associated documentation files (the |
13 # "Software"), to deal in the Software without restriction, including | 13 # "Software"), to deal in the Software without restriction, including |
14 # without limitation the rights to use, copy, modify, merge, publish, | 14 # without limitation the rights to use, copy, modify, merge, publish, |
15 # distribute, sublicense, and/or sell copies of the Software, and to | 15 # distribute, sublicense, and/or sell copies of the Software, and to |
16 # permit persons to whom the Software is furnished to do so, subject to | 16 # permit persons to whom the Software is furnished to do so, subject to |
17 # the following conditions: | 17 # the following conditions: |
18 # | 18 # |
19 # The above copyright notice and this permission notice shall be included | 19 # The above copyright notice and this permission notice shall be included |
20 # in all copies or substantial portions of the Software. | 20 # in all copies or substantial portions of the Software. |
21 # | 21 # |
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | 22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
23 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | 23 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
24 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 24 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
26 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 26 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
27 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 27 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
28 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 28 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
29 # | 29 # |
30 | 30 |
31 __revision__ = "src/engine/SCons/Executor.py 3842 2008/12/20 22:59:52 scons" | 31 __revision__ = "src/engine/SCons/Executor.py 3897 2009/01/13 06:45:54 scons" |
32 | 32 |
33 import string | 33 import string |
| 34 import UserList |
34 | 35 |
35 from SCons.Debug import logInstanceCreation | 36 from SCons.Debug import logInstanceCreation |
36 import SCons.Errors | 37 import SCons.Errors |
37 import SCons.Memoize | 38 import SCons.Memoize |
38 | 39 |
39 | 40 |
| 41 class Batch: |
| 42 """Remembers exact association between targets |
| 43 and sources of executor.""" |
| 44 def __init__(self, targets=[], sources=[]): |
| 45 self.targets = targets |
| 46 self.sources = sources |
| 47 |
| 48 |
| 49 |
| 50 class TSList(UserList.UserList): |
| 51 """A class that implements $TARGETS or $SOURCES expansions by wrapping |
| 52 an executor Method. This class is used in the Executor.lvars() |
| 53 to delay creation of NodeList objects until they're needed. |
| 54 |
| 55 Note that we subclass UserList.UserList purely so that the |
| 56 is_Sequence() function will identify an object of this class as |
| 57 a list during variable expansion. We're not really using any |
| 58 UserList.UserList methods in practice. |
| 59 """ |
| 60 def __init__(self, func): |
| 61 self.func = func |
| 62 def __getattr__(self, attr): |
| 63 nl = self.func() |
| 64 return getattr(nl, attr) |
| 65 def __getitem__(self, i): |
| 66 nl = self.func() |
| 67 return nl[i] |
| 68 def __getslice__(self, i, j): |
| 69 nl = self.func() |
| 70 i = max(i, 0); j = max(j, 0) |
| 71 return nl[i:j] |
| 72 def __str__(self): |
| 73 nl = self.func() |
| 74 return str(nl) |
| 75 def __repr__(self): |
| 76 nl = self.func() |
| 77 return repr(nl) |
| 78 |
| 79 class TSObject: |
| 80 """A class that implements $TARGET or $SOURCE expansions by wrapping |
| 81 an Executor method. |
| 82 """ |
| 83 def __init__(self, func): |
| 84 self.func = func |
| 85 def __getattr__(self, attr): |
| 86 n = self.func() |
| 87 return getattr(n, attr) |
| 88 def __str__(self): |
| 89 n = self.func() |
| 90 if n: |
| 91 return str(n) |
| 92 return '' |
| 93 def __repr__(self): |
| 94 n = self.func() |
| 95 if n: |
| 96 return repr(n) |
| 97 return '' |
| 98 |
| 99 def rfile(node): |
| 100 """ |
| 101 A function to return the results of a Node's rfile() method, |
| 102 if it exists, and the Node itself otherwise (if it's a Value |
| 103 Node, e.g.). |
| 104 """ |
| 105 try: |
| 106 rfile = node.rfile |
| 107 except AttributeError: |
| 108 return node |
| 109 else: |
| 110 return rfile() |
| 111 |
| 112 |
40 class Executor: | 113 class Executor: |
41 """A class for controlling instances of executing an action. | 114 """A class for controlling instances of executing an action. |
42 | 115 |
43 This largely exists to hold a single association of an action, | 116 This largely exists to hold a single association of an action, |
44 environment, list of environment override dictionaries, targets | 117 environment, list of environment override dictionaries, targets |
45 and sources for later processing as needed. | 118 and sources for later processing as needed. |
46 """ | 119 """ |
47 | 120 |
48 if SCons.Memoize.use_memoizer: | 121 if SCons.Memoize.use_memoizer: |
49 __metaclass__ = SCons.Memoize.Memoized_Metaclass | 122 __metaclass__ = SCons.Memoize.Memoized_Metaclass |
50 | 123 |
51 memoizer_counters = [] | 124 memoizer_counters = [] |
52 | 125 |
53 def __init__(self, action, env=None, overridelist=[{}], | 126 def __init__(self, action, env=None, overridelist=[{}], |
54 targets=[], sources=[], builder_kw={}): | 127 targets=[], sources=[], builder_kw={}): |
55 if __debug__: logInstanceCreation(self, 'Executor.Executor') | 128 if __debug__: logInstanceCreation(self, 'Executor.Executor') |
56 self.set_action_list(action) | 129 self.set_action_list(action) |
57 self.pre_actions = [] | 130 self.pre_actions = [] |
58 self.post_actions = [] | 131 self.post_actions = [] |
59 self.env = env | 132 self.env = env |
60 self.overridelist = overridelist | 133 self.overridelist = overridelist |
61 self.targets = targets | 134 if targets or sources: |
62 self.sources = sources[:] | 135 self.batches = [Batch(targets[:], sources[:])] |
63 self.sources_need_sorting = False | 136 else: |
| 137 self.batches = [] |
64 self.builder_kw = builder_kw | 138 self.builder_kw = builder_kw |
65 self._memo = {} | 139 self._memo = {} |
66 | 140 |
| 141 def get_lvars(self): |
| 142 try: |
| 143 return self.lvars |
| 144 except AttributeError: |
| 145 self.lvars = { |
| 146 'CHANGED_SOURCES' : TSList(self._get_changed_sources), |
| 147 'CHANGED_TARGETS' : TSList(self._get_changed_targets), |
| 148 'SOURCE' : TSObject(self._get_source), |
| 149 'SOURCES' : TSList(self._get_sources), |
| 150 'TARGET' : TSObject(self._get_target), |
| 151 'TARGETS' : TSList(self._get_targets), |
| 152 'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources), |
| 153 'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets), |
| 154 } |
| 155 return self.lvars |
| 156 |
| 157 def _get_changes(self): |
| 158 cs = [] |
| 159 ct = [] |
| 160 us = [] |
| 161 ut = [] |
| 162 for b in self.batches: |
| 163 if b.targets[0].changed(): |
| 164 cs.extend(map(rfile, b.sources)) |
| 165 ct.extend(b.targets) |
| 166 else: |
| 167 us.extend(map(rfile, b.sources)) |
| 168 ut.extend(b.targets) |
| 169 self._changed_sources_list = SCons.Util.NodeList(cs) |
| 170 self._changed_targets_list = SCons.Util.NodeList(ct) |
| 171 self._unchanged_sources_list = SCons.Util.NodeList(us) |
| 172 self._unchanged_targets_list = SCons.Util.NodeList(ut) |
| 173 |
| 174 def _get_changed_sources(self, *args, **kw): |
| 175 try: |
| 176 return self._changed_sources_list |
| 177 except AttributeError: |
| 178 self._get_changes() |
| 179 return self._changed_sources_list |
| 180 |
| 181 def _get_changed_targets(self, *args, **kw): |
| 182 try: |
| 183 return self._changed_targets_list |
| 184 except AttributeError: |
| 185 self._get_changes() |
| 186 return self._changed_targets_list |
| 187 |
| 188 def _get_source(self, *args, **kw): |
| 189 #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst
_proxy()]) |
| 190 return rfile(self.batches[0].sources[0]).get_subst_proxy() |
| 191 |
| 192 def _get_sources(self, *args, **kw): |
| 193 return SCons.Util.NodeList(map(lambda n: rfile(n).get_subst_proxy(), sel
f.get_all_sources())) |
| 194 |
| 195 def _get_target(self, *args, **kw): |
| 196 #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy(
)]) |
| 197 return self.batches[0].targets[0].get_subst_proxy() |
| 198 |
| 199 def _get_targets(self, *args, **kw): |
| 200 return SCons.Util.NodeList(map(lambda n: n.get_subst_proxy(), self.get_a
ll_targets())) |
| 201 |
| 202 def _get_unchanged_sources(self, *args, **kw): |
| 203 try: |
| 204 return self._unchanged_sources_list |
| 205 except AttributeError: |
| 206 self._get_changes() |
| 207 return self._unchanged_sources_list |
| 208 |
| 209 def _get_unchanged_targets(self, *args, **kw): |
| 210 try: |
| 211 return self._unchanged_targets_list |
| 212 except AttributeError: |
| 213 self._get_changes() |
| 214 return self._unchanged_targets_list |
| 215 |
| 216 def get_action_targets(self): |
| 217 if not self.action_list: |
| 218 return [] |
| 219 targets_string = self.action_list[0].get_targets(self.env, self) |
| 220 if targets_string[0] == '$': |
| 221 targets_string = targets_string[1:] |
| 222 return self.get_lvars()[targets_string] |
| 223 |
67 def set_action_list(self, action): | 224 def set_action_list(self, action): |
68 import SCons.Util | 225 import SCons.Util |
69 if not SCons.Util.is_List(action): | 226 if not SCons.Util.is_List(action): |
70 if not action: | 227 if not action: |
71 import SCons.Errors | 228 import SCons.Errors |
72 raise SCons.Errors.UserError, "Executor must have an action." | 229 raise SCons.Errors.UserError, "Executor must have an action." |
73 action = [action] | 230 action = [action] |
74 self.action_list = action | 231 self.action_list = action |
75 | 232 |
76 def get_action_list(self): | 233 def get_action_list(self): |
77 return self.pre_actions + self.action_list + self.post_actions | 234 return self.pre_actions + self.action_list + self.post_actions |
78 | 235 |
| 236 def get_all_targets(self): |
| 237 """Returns all targets for all batches of this Executor.""" |
| 238 result = [] |
| 239 for batch in self.batches: |
| 240 # TODO(1.5): remove the list() cast |
| 241 result.extend(list(batch.targets)) |
| 242 return result |
| 243 |
| 244 def get_all_sources(self): |
| 245 """Returns all sources for all batches of this Executor.""" |
| 246 result = [] |
| 247 for batch in self.batches: |
| 248 # TODO(1.5): remove the list() cast |
| 249 result.extend(list(batch.sources)) |
| 250 return result |
| 251 |
| 252 def get_all_children(self): |
| 253 """Returns all unique children (dependencies) for all batches |
| 254 of this Executor. |
| 255 |
| 256 The Taskmaster can recognize when it's already evaluated a |
| 257 Node, so we don't have to make this list unique for its intended |
| 258 canonical use case, but we expect there to be a lot of redundancy |
| 259 (long lists of batched .cc files #including the same .h files |
| 260 over and over), so removing the duplicates once up front should |
| 261 save the Taskmaster a lot of work. |
| 262 """ |
| 263 result = SCons.Util.UniqueList([]) |
| 264 for target in self.get_all_targets(): |
| 265 result.extend(target.children()) |
| 266 return result |
| 267 |
| 268 def get_all_prerequisites(self): |
| 269 """Returns all unique (order-only) prerequisites for all batches |
| 270 of this Executor. |
| 271 """ |
| 272 result = SCons.Util.UniqueList([]) |
| 273 for target in self.get_all_targets(): |
| 274 # TODO(1.5): remove the list() cast |
| 275 result.extend(list(target.prerequisites)) |
| 276 return result |
| 277 |
| 278 def get_action_side_effects(self): |
| 279 |
| 280 """Returns all side effects for all batches of this |
| 281 Executor used by the underlying Action. |
| 282 """ |
| 283 result = SCons.Util.UniqueList([]) |
| 284 for target in self.get_action_targets(): |
| 285 result.extend(target.side_effects) |
| 286 return result |
| 287 |
79 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) | 288 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) |
80 | 289 |
81 def get_build_env(self): | 290 def get_build_env(self): |
82 """Fetch or create the appropriate build Environment | 291 """Fetch or create the appropriate build Environment |
83 for this Executor. | 292 for this Executor. |
84 """ | 293 """ |
85 try: | 294 try: |
86 return self._memo['get_build_env'] | 295 return self._memo['get_build_env'] |
87 except KeyError: | 296 except KeyError: |
88 pass | 297 pass |
(...skipping 13 matching lines...) Expand all Loading... |
102 | 311 |
103 self._memo['get_build_env'] = build_env | 312 self._memo['get_build_env'] = build_env |
104 | 313 |
105 return build_env | 314 return build_env |
106 | 315 |
107 def get_build_scanner_path(self, scanner): | 316 def get_build_scanner_path(self, scanner): |
108 """Fetch the scanner path for this executor's targets and sources. | 317 """Fetch the scanner path for this executor's targets and sources. |
109 """ | 318 """ |
110 env = self.get_build_env() | 319 env = self.get_build_env() |
111 try: | 320 try: |
112 cwd = self.targets[0].cwd | 321 cwd = self.batches[0].targets[0].cwd |
113 except (IndexError, AttributeError): | 322 except (IndexError, AttributeError): |
114 cwd = None | 323 cwd = None |
115 return scanner.path(env, cwd, self.targets, self.get_sources()) | 324 return scanner.path(env, cwd, |
| 325 self.get_all_targets(), |
| 326 self.get_all_sources()) |
116 | 327 |
117 def get_kw(self, kw={}): | 328 def get_kw(self, kw={}): |
118 result = self.builder_kw.copy() | 329 result = self.builder_kw.copy() |
119 result.update(kw) | 330 result.update(kw) |
| 331 result['executor'] = self |
120 return result | 332 return result |
121 | 333 |
122 def do_nothing(self, target, kw): | 334 def do_nothing(self, target, kw): |
123 return 0 | 335 return 0 |
124 | 336 |
125 def do_execute(self, target, kw): | 337 def do_execute(self, target, kw): |
126 """Actually execute the action list.""" | 338 """Actually execute the action list.""" |
127 env = self.get_build_env() | 339 env = self.get_build_env() |
128 kw = self.get_kw(kw) | 340 kw = self.get_kw(kw) |
129 status = 0 | 341 status = 0 |
130 for act in self.get_action_list(): | 342 for act in self.get_action_list(): |
131 status = apply(act, (self.targets, self.get_sources(), env), kw) | 343 #args = (self.get_all_targets(), self.get_all_sources(), env) |
| 344 args = ([], [], env) |
| 345 status = apply(act, args, kw) |
132 if isinstance(status, SCons.Errors.BuildError): | 346 if isinstance(status, SCons.Errors.BuildError): |
133 status.executor = self | 347 status.executor = self |
134 raise status | 348 raise status |
135 elif status: | 349 elif status: |
136 msg = "Error %s" % status | 350 msg = "Error %s" % status |
137 raise SCons.Errors.BuildError( | 351 raise SCons.Errors.BuildError( |
138 errstr=msg, | 352 errstr=msg, |
139 node=self.targets, | 353 node=self.batches[0].targets, |
140 executor=self, | 354 executor=self, |
141 action=act) | 355 action=act) |
142 return status | 356 return status |
143 | 357 |
144 # use extra indirection because with new-style objects (Python 2.2 | 358 # use extra indirection because with new-style objects (Python 2.2 |
145 # and above) we can't override special methods, and nullify() needs | 359 # and above) we can't override special methods, and nullify() needs |
146 # to be able to do this. | 360 # to be able to do this. |
147 | 361 |
148 def __call__(self, target, **kw): | 362 def __call__(self, target, **kw): |
149 return self.do_execute(target, kw) | 363 return self.do_execute(target, kw) |
150 | 364 |
151 def cleanup(self): | 365 def cleanup(self): |
152 self._memo = {} | 366 self._memo = {} |
153 | 367 |
154 def add_sources(self, sources): | 368 def add_sources(self, sources): |
155 """Add source files to this Executor's list. This is necessary | 369 """Add source files to this Executor's list. This is necessary |
156 for "multi" Builders that can be called repeatedly to build up | 370 for "multi" Builders that can be called repeatedly to build up |
157 a source file list for a given target.""" | 371 a source file list for a given target.""" |
158 self.sources.extend(sources) | 372 # TODO(batch): extend to multiple batches |
159 self.sources_need_sorting = True | 373 assert (len(self.batches) == 1) |
| 374 # TODO(batch): remove duplicates? |
| 375 sources = filter(lambda x, s=self.batches[0].sources: x not in s, source
s) |
| 376 self.batches[0].sources.extend(sources) |
160 | 377 |
161 def get_sources(self): | 378 def get_sources(self): |
162 if self.sources_need_sorting: | 379 return self.batches[0].sources |
163 self.sources = SCons.Util.uniquer_hashables(self.sources) | 380 |
164 self.sources_need_sorting = False | 381 def add_batch(self, targets, sources): |
165 return self.sources | 382 """Add pair of associated target and source to this Executor's list. |
| 383 This is necessary for "batch" Builders that can be called repeatedly |
| 384 to build up a list of matching target and source files that will be |
| 385 used in order to update multiple target files at once from multiple |
| 386 corresponding source files, for tools like MSVC that support it.""" |
| 387 self.batches.append(Batch(targets, sources)) |
166 | 388 |
167 def prepare(self): | 389 def prepare(self): |
168 """ | 390 """ |
169 Preparatory checks for whether this Executor can go ahead | 391 Preparatory checks for whether this Executor can go ahead |
170 and (try to) build its targets. | 392 and (try to) build its targets. |
171 """ | 393 """ |
172 for s in self.get_sources(): | 394 for s in self.get_all_sources(): |
173 if s.missing(): | 395 if s.missing(): |
174 msg = "Source `%s' not found, needed by target `%s'." | 396 msg = "Source `%s' not found, needed by target `%s'." |
175 raise SCons.Errors.StopError, msg % (s, self.targets[0]) | 397 raise SCons.Errors.StopError, msg % (s, self.batches[0].targets[
0]) |
176 | 398 |
177 def add_pre_action(self, action): | 399 def add_pre_action(self, action): |
178 self.pre_actions.append(action) | 400 self.pre_actions.append(action) |
179 | 401 |
180 def add_post_action(self, action): | 402 def add_post_action(self, action): |
181 self.post_actions.append(action) | 403 self.post_actions.append(action) |
182 | 404 |
183 # another extra indirection for new-style objects and nullify... | 405 # another extra indirection for new-style objects and nullify... |
184 | 406 |
185 def my_str(self): | 407 def my_str(self): |
186 env = self.get_build_env() | 408 env = self.get_build_env() |
187 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ | 409 get = lambda action, t=self.get_all_targets(), s=self.get_all_sources(),
e=env: \ |
188 action.genstring(t, s, e) | 410 action.genstring(t, s, e) |
189 return string.join(map(get, self.get_action_list()), "\n") | 411 return string.join(map(get, self.get_action_list()), "\n") |
190 | 412 |
191 | 413 |
192 def __str__(self): | 414 def __str__(self): |
193 return self.my_str() | 415 return self.my_str() |
194 | 416 |
195 def nullify(self): | 417 def nullify(self): |
196 self.cleanup() | 418 self.cleanup() |
197 self.do_execute = self.do_nothing | 419 self.do_execute = self.do_nothing |
198 self.my_str = lambda S=self: '' | 420 self.my_str = lambda S=self: '' |
199 | 421 |
200 memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) | 422 memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) |
201 | 423 |
202 def get_contents(self): | 424 def get_contents(self): |
203 """Fetch the signature contents. This is the main reason this | 425 """Fetch the signature contents. This is the main reason this |
204 class exists, so we can compute this once and cache it regardless | 426 class exists, so we can compute this once and cache it regardless |
205 of how many target or source Nodes there are. | 427 of how many target or source Nodes there are. |
206 """ | 428 """ |
207 try: | 429 try: |
208 return self._memo['get_contents'] | 430 return self._memo['get_contents'] |
209 except KeyError: | 431 except KeyError: |
210 pass | 432 pass |
211 env = self.get_build_env() | 433 env = self.get_build_env() |
212 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ | 434 get = lambda action, t=self.get_all_targets(), s=self.get_all_sources(),
e=env: \ |
213 action.get_contents(t, s, e) | 435 action.get_contents(t, s, e) |
214 result = string.join(map(get, self.get_action_list()), "") | 436 result = string.join(map(get, self.get_action_list()), "") |
215 self._memo['get_contents'] = result | 437 self._memo['get_contents'] = result |
216 return result | 438 return result |
217 | 439 |
218 def get_timestamp(self): | 440 def get_timestamp(self): |
219 """Fetch a time stamp for this Executor. We don't have one, of | 441 """Fetch a time stamp for this Executor. We don't have one, of |
220 course (only files do), but this is the interface used by the | 442 course (only files do), but this is the interface used by the |
221 timestamp module. | 443 timestamp module. |
222 """ | 444 """ |
223 return 0 | 445 return 0 |
224 | 446 |
225 def scan_targets(self, scanner): | 447 def scan_targets(self, scanner): |
226 self.scan(scanner, self.targets) | 448 # TODO(batch): scan by batches |
| 449 self.scan(scanner, self.get_all_targets()) |
227 | 450 |
228 def scan_sources(self, scanner): | 451 def scan_sources(self, scanner): |
229 if self.sources: | 452 # TODO(batch): scan by batches |
230 self.scan(scanner, self.get_sources()) | 453 if self.batches[0].sources: |
| 454 self.scan(scanner, self.get_all_sources()) |
231 | 455 |
232 def scan(self, scanner, node_list): | 456 def scan(self, scanner, node_list): |
233 """Scan a list of this Executor's files (targets or sources) for | 457 """Scan a list of this Executor's files (targets or sources) for |
234 implicit dependencies and update all of the targets with them. | 458 implicit dependencies and update all of the targets with them. |
235 This essentially short-circuits an N*M scan of the sources for | 459 This essentially short-circuits an N*M scan of the sources for |
236 each individual target, which is a hell of a lot more efficient. | 460 each individual target, which is a hell of a lot more efficient. |
237 """ | 461 """ |
238 env = self.get_build_env() | 462 env = self.get_build_env() |
239 | 463 |
| 464 # TODO(batch): scan by batches) |
240 deps = [] | 465 deps = [] |
241 if scanner: | 466 if scanner: |
242 for node in node_list: | 467 for node in node_list: |
243 node.disambiguate() | 468 node.disambiguate() |
244 s = scanner.select(node) | 469 s = scanner.select(node) |
245 if not s: | 470 if not s: |
246 continue | 471 continue |
247 path = self.get_build_scanner_path(s) | 472 path = self.get_build_scanner_path(s) |
248 deps.extend(node.get_implicit_deps(env, s, path)) | 473 deps.extend(node.get_implicit_deps(env, s, path)) |
249 else: | 474 else: |
250 kw = self.get_kw() | 475 kw = self.get_kw() |
251 for node in node_list: | 476 for node in node_list: |
252 node.disambiguate() | 477 node.disambiguate() |
253 scanner = node.get_env_scanner(env, kw) | 478 scanner = node.get_env_scanner(env, kw) |
254 if not scanner: | 479 if not scanner: |
255 continue | 480 continue |
256 scanner = scanner.select(node) | 481 scanner = scanner.select(node) |
257 if not scanner: | 482 if not scanner: |
258 continue | 483 continue |
259 path = self.get_build_scanner_path(scanner) | 484 path = self.get_build_scanner_path(scanner) |
260 deps.extend(node.get_implicit_deps(env, scanner, path)) | 485 deps.extend(node.get_implicit_deps(env, scanner, path)) |
261 | 486 |
262 deps.extend(self.get_implicit_deps()) | 487 deps.extend(self.get_implicit_deps()) |
263 | 488 |
264 for tgt in self.targets: | 489 for tgt in self.get_all_targets(): |
265 tgt.add_to_implicit(deps) | 490 tgt.add_to_implicit(deps) |
266 | 491 |
267 def _get_unignored_sources_key(self, ignore=()): | 492 def _get_unignored_sources_key(self, node, ignore=()): |
268 return tuple(ignore) | 493 return (node,) + tuple(ignore) |
269 | 494 |
270 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _g
et_unignored_sources_key)) | 495 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _g
et_unignored_sources_key)) |
271 | 496 |
272 def get_unignored_sources(self, ignore=()): | 497 def get_unignored_sources(self, node, ignore=()): |
273 ignore = tuple(ignore) | 498 key = (node,) + tuple(ignore) |
274 try: | 499 try: |
275 memo_dict = self._memo['get_unignored_sources'] | 500 memo_dict = self._memo['get_unignored_sources'] |
276 except KeyError: | 501 except KeyError: |
277 memo_dict = {} | 502 memo_dict = {} |
278 self._memo['get_unignored_sources'] = memo_dict | 503 self._memo['get_unignored_sources'] = memo_dict |
279 else: | 504 else: |
280 try: | 505 try: |
281 return memo_dict[ignore] | 506 return memo_dict[key] |
282 except KeyError: | 507 except KeyError: |
283 pass | 508 pass |
284 | 509 |
285 sourcelist = self.get_sources() | 510 if node: |
| 511 # TODO: better way to do this (it's a linear search, |
| 512 # but it may not be critical path)? |
| 513 sourcelist = [] |
| 514 for b in self.batches: |
| 515 if node in b.targets: |
| 516 sourcelist = b.sources |
| 517 break |
| 518 else: |
| 519 sourcelist = self.get_all_sources() |
286 if ignore: | 520 if ignore: |
287 idict = {} | 521 idict = {} |
288 for i in ignore: | 522 for i in ignore: |
289 idict[i] = 1 | 523 idict[i] = 1 |
290 sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist) | 524 sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist) |
291 | 525 |
292 memo_dict[ignore] = sourcelist | 526 memo_dict[key] = sourcelist |
293 | 527 |
294 return sourcelist | 528 return sourcelist |
295 | 529 |
296 def _process_sources_key(self, func, ignore=()): | |
297 return (func, tuple(ignore)) | |
298 | |
299 memoizer_counters.append(SCons.Memoize.CountDict('process_sources', _process
_sources_key)) | |
300 | |
301 def process_sources(self, func, ignore=()): | |
302 memo_key = (func, tuple(ignore)) | |
303 try: | |
304 memo_dict = self._memo['process_sources'] | |
305 except KeyError: | |
306 memo_dict = {} | |
307 self._memo['process_sources'] = memo_dict | |
308 else: | |
309 try: | |
310 return memo_dict[memo_key] | |
311 except KeyError: | |
312 pass | |
313 | |
314 result = map(func, self.get_unignored_sources(ignore)) | |
315 | |
316 memo_dict[memo_key] = result | |
317 | |
318 return result | |
319 | |
320 def get_implicit_deps(self): | 530 def get_implicit_deps(self): |
321 """Return the executor's implicit dependencies, i.e. the nodes of | 531 """Return the executor's implicit dependencies, i.e. the nodes of |
322 the commands to be executed.""" | 532 the commands to be executed.""" |
323 result = [] | 533 result = [] |
324 build_env = self.get_build_env() | 534 build_env = self.get_build_env() |
325 for act in self.get_action_list(): | 535 for act in self.get_action_list(): |
326 result.extend(act.get_implicit_deps(self.targets, self.get_sources()
, build_env)) | 536 deps = act.get_implicit_deps(self.get_all_targets(), |
| 537 self.get_all_sources(), |
| 538 build_env) |
| 539 result.extend(deps) |
327 return result | 540 return result |
328 | 541 |
| 542 |
| 543 |
| 544 _batch_executors = {} |
| 545 |
| 546 def GetBatchExecutor(key): |
| 547 return _batch_executors[key] |
| 548 |
| 549 def AddBatchExecutor(key, executor): |
| 550 assert not _batch_executors.has_key(key) |
| 551 _batch_executors[key] = executor |
| 552 |
329 nullenv = None | 553 nullenv = None |
330 | 554 |
| 555 |
331 def get_NullEnvironment(): | 556 def get_NullEnvironment(): |
332 """Use singleton pattern for Null Environments.""" | 557 """Use singleton pattern for Null Environments.""" |
333 global nullenv | 558 global nullenv |
334 | 559 |
335 import SCons.Util | 560 import SCons.Util |
336 class NullEnvironment(SCons.Util.Null): | 561 class NullEnvironment(SCons.Util.Null): |
337 import SCons.CacheDir | 562 import SCons.CacheDir |
338 _CacheDir_path = None | 563 _CacheDir_path = None |
339 _CacheDir = SCons.CacheDir.CacheDir(None) | 564 _CacheDir = SCons.CacheDir.CacheDir(None) |
340 def get_CacheDir(self): | 565 def get_CacheDir(self): |
341 return self._CacheDir | 566 return self._CacheDir |
342 | 567 |
343 if not nullenv: | 568 if not nullenv: |
344 nullenv = NullEnvironment() | 569 nullenv = NullEnvironment() |
345 return nullenv | 570 return nullenv |
346 | 571 |
347 class Null: | 572 class Null: |
348 """A null Executor, with a null build Environment, that does | 573 """A null Executor, with a null build Environment, that does |
349 nothing when the rest of the methods call it. | 574 nothing when the rest of the methods call it. |
350 | 575 |
351 This might be able to disapper when we refactor things to | 576 This might be able to disapper when we refactor things to |
352 disassociate Builders from Nodes entirely, so we're not | 577 disassociate Builders from Nodes entirely, so we're not |
353 going to worry about unit tests for this--at least for now. | 578 going to worry about unit tests for this--at least for now. |
354 """ | 579 """ |
355 def __init__(self, *args, **kw): | 580 def __init__(self, *args, **kw): |
356 if __debug__: logInstanceCreation(self, 'Executor.Null') | 581 if __debug__: logInstanceCreation(self, 'Executor.Null') |
357 self.targets = kw['targets'] | 582 self.batches = [Batch(kw['targets'][:], [])] |
358 def get_build_env(self): | 583 def get_build_env(self): |
359 return get_NullEnvironment() | 584 return get_NullEnvironment() |
360 def get_build_scanner_path(self): | 585 def get_build_scanner_path(self): |
361 return None | 586 return None |
362 def cleanup(self): | 587 def cleanup(self): |
363 pass | 588 pass |
364 def prepare(self): | 589 def prepare(self): |
365 pass | 590 pass |
366 def get_unignored_sources(self, *args, **kw): | 591 def get_unignored_sources(self, *args, **kw): |
367 return tuple(()) | 592 return tuple(()) |
| 593 def get_action_targets(self): |
| 594 return [] |
368 def get_action_list(self): | 595 def get_action_list(self): |
369 return [] | 596 return [] |
| 597 def get_all_targets(self): |
| 598 return self.batches[0].targets |
| 599 def get_all_sources(self): |
| 600 return self.batches[0].targets[0].sources |
| 601 def get_all_children(self): |
| 602 return self.get_all_sources() |
| 603 def get_all_prerequisites(self): |
| 604 return [] |
| 605 def get_action_side_effects(self): |
| 606 return [] |
370 def __call__(self, *args, **kw): | 607 def __call__(self, *args, **kw): |
371 return 0 | 608 return 0 |
372 def get_contents(self): | 609 def get_contents(self): |
373 return '' | 610 return '' |
374 | |
375 def _morph(self): | 611 def _morph(self): |
376 """Morph this Null executor to a real Executor object.""" | 612 """Morph this Null executor to a real Executor object.""" |
| 613 batches = self.batches |
377 self.__class__ = Executor | 614 self.__class__ = Executor |
378 self.__init__([], targets=self.targets) | 615 self.__init__([]) |
| 616 self.batches = batches |
379 | 617 |
380 # The following methods require morphing this Null Executor to a | 618 # The following methods require morphing this Null Executor to a |
381 # real Executor object. | 619 # real Executor object. |
382 | 620 |
383 def add_pre_action(self, action): | 621 def add_pre_action(self, action): |
384 self._morph() | 622 self._morph() |
385 self.add_pre_action(action) | 623 self.add_pre_action(action) |
386 def add_post_action(self, action): | 624 def add_post_action(self, action): |
387 self._morph() | 625 self._morph() |
388 self.add_post_action(action) | 626 self.add_post_action(action) |
389 def set_action_list(self, action): | 627 def set_action_list(self, action): |
390 self._morph() | 628 self._morph() |
391 self.set_action_list(action) | 629 self.set_action_list(action) |
392 | 630 |
393 | 631 |
OLD | NEW |