Add some documentation to the SCons-version-switching hack
[senf.git] / scons / scons-1.2.0 / engine / SCons / Executor.py
1 """SCons.Executor
2
3 A module for executing actions with specific lists of target and source
4 Nodes.
5
6 """
7
8 #
9 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining
12 # a copy of this software and associated documentation files (the
13 # "Software"), to deal in the Software without restriction, including
14 # without limitation the rights to use, copy, modify, merge, publish,
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
17 # the following conditions:
18 #
19 # The above copyright notice and this permission notice shall be included
20 # in all copies or substantial portions of the Software.
21 #
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
23 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
24 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
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
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.
29 #
30
31 __revision__ = "src/engine/SCons/Executor.py 3842 2008/12/20 22:59:52 scons"
32
33 import string
34
35 from SCons.Debug import logInstanceCreation
36 import SCons.Errors
37 import SCons.Memoize
38
39
40 class Executor:
41     """A class for controlling instances of executing an action.
42
43     This largely exists to hold a single association of an action,
44     environment, list of environment override dictionaries, targets
45     and sources for later processing as needed.
46     """
47
48     if SCons.Memoize.use_memoizer:
49         __metaclass__ = SCons.Memoize.Memoized_Metaclass
50
51     memoizer_counters = []
52
53     def __init__(self, action, env=None, overridelist=[{}],
54                  targets=[], sources=[], builder_kw={}):
55         if __debug__: logInstanceCreation(self, 'Executor.Executor')
56         self.set_action_list(action)
57         self.pre_actions = []
58         self.post_actions = []
59         self.env = env
60         self.overridelist = overridelist
61         self.targets = targets
62         self.sources = sources[:]
63         self.sources_need_sorting = False
64         self.builder_kw = builder_kw
65         self._memo = {}
66
67     def set_action_list(self, action):
68         import SCons.Util
69         if not SCons.Util.is_List(action):
70             if not action:
71                 import SCons.Errors
72                 raise SCons.Errors.UserError, "Executor must have an action."
73             action = [action]
74         self.action_list = action
75
76     def get_action_list(self):
77         return self.pre_actions + self.action_list + self.post_actions
78
79     memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
80
81     def get_build_env(self):
82         """Fetch or create the appropriate build Environment
83         for this Executor.
84         """
85         try:
86             return self._memo['get_build_env']
87         except KeyError:
88             pass
89
90         # Create the build environment instance with appropriate
91         # overrides.  These get evaluated against the current
92         # environment's construction variables so that users can
93         # add to existing values by referencing the variable in
94         # the expansion.
95         overrides = {}
96         for odict in self.overridelist:
97             overrides.update(odict)
98
99         import SCons.Defaults
100         env = self.env or SCons.Defaults.DefaultEnvironment()
101         build_env = env.Override(overrides)
102
103         self._memo['get_build_env'] = build_env
104
105         return build_env
106
107     def get_build_scanner_path(self, scanner):
108         """Fetch the scanner path for this executor's targets and sources.
109         """
110         env = self.get_build_env()
111         try:
112             cwd = self.targets[0].cwd
113         except (IndexError, AttributeError):
114             cwd = None
115         return scanner.path(env, cwd, self.targets, self.get_sources())
116
117     def get_kw(self, kw={}):
118         result = self.builder_kw.copy()
119         result.update(kw)
120         return result
121
122     def do_nothing(self, target, kw):
123         return 0
124
125     def do_execute(self, target, kw):
126         """Actually execute the action list."""
127         env = self.get_build_env()
128         kw = self.get_kw(kw)
129         status = 0
130         for act in self.get_action_list():
131             status = apply(act, (self.targets, self.get_sources(), env), kw)
132             if isinstance(status, SCons.Errors.BuildError):
133                 status.executor = self
134                 raise status
135             elif status:
136                 msg = "Error %s" % status
137                 raise SCons.Errors.BuildError(
138                     errstr=msg, 
139                     node=self.targets,
140                     executor=self, 
141                     action=act)
142         return status
143
144     # use extra indirection because with new-style objects (Python 2.2
145     # and above) we can't override special methods, and nullify() needs
146     # to be able to do this.
147
148     def __call__(self, target, **kw):
149         return self.do_execute(target, kw)
150
151     def cleanup(self):
152         self._memo = {}
153
154     def add_sources(self, sources):
155         """Add source files to this Executor's list.  This is necessary
156         for "multi" Builders that can be called repeatedly to build up
157         a source file list for a given target."""
158         self.sources.extend(sources)
159         self.sources_need_sorting = True
160
161     def get_sources(self):
162         if self.sources_need_sorting:
163             self.sources = SCons.Util.uniquer_hashables(self.sources)
164             self.sources_need_sorting = False
165         return self.sources
166
167     def prepare(self):
168         """
169         Preparatory checks for whether this Executor can go ahead
170         and (try to) build its targets.
171         """
172         for s in self.get_sources():
173             if s.missing():
174                 msg = "Source `%s' not found, needed by target `%s'."
175                 raise SCons.Errors.StopError, msg % (s, self.targets[0])
176
177     def add_pre_action(self, action):
178         self.pre_actions.append(action)
179
180     def add_post_action(self, action):
181         self.post_actions.append(action)
182
183     # another extra indirection for new-style objects and nullify...
184
185     def my_str(self):
186         env = self.get_build_env()
187         get = lambda action, t=self.targets, s=self.get_sources(), e=env: \
188                      action.genstring(t, s, e)
189         return string.join(map(get, self.get_action_list()), "\n")
190
191
192     def __str__(self):
193         return self.my_str()
194
195     def nullify(self):
196         self.cleanup()
197         self.do_execute = self.do_nothing
198         self.my_str     = lambda S=self: ''
199
200     memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
201
202     def get_contents(self):
203         """Fetch the signature contents.  This is the main reason this
204         class exists, so we can compute this once and cache it regardless
205         of how many target or source Nodes there are.
206         """
207         try:
208             return self._memo['get_contents']
209         except KeyError:
210             pass
211         env = self.get_build_env()
212         get = lambda action, t=self.targets, s=self.get_sources(), e=env: \
213                      action.get_contents(t, s, e)
214         result = string.join(map(get, self.get_action_list()), "")
215         self._memo['get_contents'] = result
216         return result
217
218     def get_timestamp(self):
219         """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
221         timestamp module.
222         """
223         return 0
224
225     def scan_targets(self, scanner):
226         self.scan(scanner, self.targets)
227
228     def scan_sources(self, scanner):
229         if self.sources:
230             self.scan(scanner, self.get_sources())
231
232     def scan(self, scanner, node_list):
233         """Scan a list of this Executor's files (targets or sources) for
234         implicit dependencies and update all of the targets with them.
235         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.
237         """
238         env = self.get_build_env()
239
240         deps = []
241         if scanner:
242             for node in node_list:
243                 node.disambiguate()
244                 s = scanner.select(node)
245                 if not s:
246                     continue
247                 path = self.get_build_scanner_path(s)
248                 deps.extend(node.get_implicit_deps(env, s, path))
249         else:
250             kw = self.get_kw()
251             for node in node_list:
252                 node.disambiguate()
253                 scanner = node.get_env_scanner(env, kw)
254                 if not scanner:
255                     continue
256                 scanner = scanner.select(node)
257                 if not scanner:
258                     continue
259                 path = self.get_build_scanner_path(scanner)
260                 deps.extend(node.get_implicit_deps(env, scanner, path))
261
262         deps.extend(self.get_implicit_deps())
263
264         for tgt in self.targets:
265             tgt.add_to_implicit(deps)
266
267     def _get_unignored_sources_key(self, ignore=()):
268         return tuple(ignore)
269
270     memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
271
272     def get_unignored_sources(self, ignore=()):
273         ignore = tuple(ignore)
274         try:
275             memo_dict = self._memo['get_unignored_sources']
276         except KeyError:
277             memo_dict = {}
278             self._memo['get_unignored_sources'] = memo_dict
279         else:
280             try:
281                 return memo_dict[ignore]
282             except KeyError:
283                 pass
284
285         sourcelist = self.get_sources()
286         if ignore:
287             idict = {}
288             for i in ignore:
289                 idict[i] = 1
290             sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist)
291
292         memo_dict[ignore] = sourcelist
293
294         return sourcelist
295
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):
321         """Return the executor's implicit dependencies, i.e. the nodes of
322         the commands to be executed."""
323         result = []
324         build_env = self.get_build_env()
325         for act in self.get_action_list():
326             result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env))
327         return result
328
329 nullenv = None
330
331 def get_NullEnvironment():
332     """Use singleton pattern for Null Environments."""
333     global nullenv
334
335     import SCons.Util
336     class NullEnvironment(SCons.Util.Null):
337         import SCons.CacheDir
338         _CacheDir_path = None
339         _CacheDir = SCons.CacheDir.CacheDir(None)
340         def get_CacheDir(self):
341             return self._CacheDir
342
343     if not nullenv:
344         nullenv = NullEnvironment()
345     return nullenv
346
347 class Null:
348     """A null Executor, with a null build Environment, that does
349     nothing when the rest of the methods call it.
350
351     This might be able to disapper when we refactor things to
352     disassociate Builders from Nodes entirely, so we're not
353     going to worry about unit tests for this--at least for now.
354     """
355     def __init__(self, *args, **kw):
356         if __debug__: logInstanceCreation(self, 'Executor.Null')
357         self.targets = kw['targets']
358     def get_build_env(self):
359         return get_NullEnvironment()
360     def get_build_scanner_path(self):
361         return None
362     def cleanup(self):
363         pass
364     def prepare(self):
365         pass
366     def get_unignored_sources(self, *args, **kw):
367         return tuple(())
368     def get_action_list(self):
369         return []
370     def __call__(self, *args, **kw):
371         return 0
372     def get_contents(self):
373         return ''
374
375     def _morph(self):
376         """Morph this Null executor to a real Executor object."""
377         self.__class__ = Executor
378         self.__init__([], targets=self.targets)            
379
380     # The following methods require morphing this Null Executor to a
381     # real Executor object.
382
383     def add_pre_action(self, action):
384         self._morph()
385         self.add_pre_action(action)
386     def add_post_action(self, action):
387         self._morph()
388         self.add_post_action(action)
389     def set_action_list(self, action):
390         self._morph()
391         self.set_action_list(action)
392
393