Multi-level build dir with Object() and duplicate=0.
[scons.git] / src / engine / SCons / Builder.py
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 Node (file) from other Nodes (files), and
7 how to create those dependencies for tracking.
8
9 The main entry point here is the Builder() factory method.  This
10 provides a procedural interface that creates the right underlying
11 Builder object based on the keyword arguments supplied and the types of
12 the arguments.
13
14 The goal is for this external interface to be simple enough that the
15 vast majority of users can create new Builders as necessary to support
16 building new types of files in their configurations, without having to
17 dive any deeper into this subsystem.
18
19 """
20
21 #
22 # __COPYRIGHT__
23 #
24 # Permission is hereby granted, free of charge, to any person obtaining
25 # a copy of this software and associated documentation files (the
26 # "Software"), to deal in the Software without restriction, including
27 # without limitation the rights to use, copy, modify, merge, publish,
28 # distribute, sublicense, and/or sell copies of the Software, and to
29 # permit persons to whom the Software is furnished to do so, subject to
30 # the following conditions:
31 #
32 # The above copyright notice and this permission notice shall be included
33 # in all copies or substantial portions of the Software.
34 #
35 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
36 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
37 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
38 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
39 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
40 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
41 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 #
43
44 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
45
46 import os.path
47 import UserDict
48
49 import SCons.Action
50 from SCons.Errors import InternalError, UserError
51 import SCons.Executor
52 import SCons.Node
53 import SCons.Node.FS
54 import SCons.Util
55 import SCons.Warnings
56
57 class _Null:
58     pass
59
60 _null = _Null
61
62 class DictCmdGenerator:
63     """This is a callable class that can be used as a
64     command generator function.  It holds on to a dictionary
65     mapping file suffixes to Actions.  It uses that dictionary
66     to return the proper action based on the file suffix of
67     the source file."""
68     
69     def __init__(self, action_dict):
70         self.action_dict = action_dict
71
72     def src_suffixes(self):
73         return self.action_dict.keys()
74
75     def add_action(self, suffix, action):
76         """Add a suffix-action pair to the mapping.
77         """
78         self.action_dict[suffix] = action
79
80     def __call__(self, target, source, env, for_signature):
81         ext = None
82         for src in map(str, source):
83             my_ext = SCons.Util.splitext(src)[1]
84             if ext and my_ext != ext:
85                 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
86             ext = my_ext
87
88         if not ext:
89             raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
90         try:
91             return self.action_dict[ext]
92         except KeyError:
93             # Before raising the user error, try to perform Environment
94             # substitution on the keys of action_dict.
95             s_dict = {}
96             for (k,v) in self.action_dict.items():
97                 s_k = env.subst(k)
98                 if s_dict.has_key(s_k):
99                     # XXX Note that we do only raise errors, when variables
100                     # point to the same suffix. If one suffix is a
101                     # literal and a variable suffix contains this literal
102                     # we don't raise an error (cause the literal 'wins')
103                     raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (s_dict[s_k][0], k, s_k))
104                 s_dict[s_k] = (k,v)
105             try:
106                 return s_dict[ext][1]
107             except KeyError:
108                 raise UserError("While building `%s': Don't know how to build a file with suffix %s." % (repr(map(str, target)), repr(ext)))
109
110     def __cmp__(self, other):
111         return cmp(self.action_dict, other.action_dict)
112
113 class DictEmitter(UserDict.UserDict):
114     """A callable dictionary that maps file suffixes to emitters.
115     When called, it finds the right emitter in its dictionary for the
116     suffix of the first source file, and calls that emitter to get the
117     right lists of targets and sources to return.  If there's no emitter
118     for the suffix in its dictionary, the original target and source are
119     returned.
120     """
121     def __call__(self, target, source, env):
122         ext = SCons.Util.splitext(str(source[0]))[1]
123         if ext:
124             try:
125                 emitter = self[ext]
126             except KeyError:
127                 # Before raising the user error, try to perform Environment
128                 # substitution on the keys of emitter_dict.
129                 s_dict = {}
130                 for (k,v) in self.items():
131                     s_k = env.subst(k)
132                     s_dict[s_k] = v
133                 try:
134                     emitter = s_dict[ext]
135                 except KeyError:
136                     emitter = None
137             if emitter:
138                 target, source = emitter(target, source, env)
139         return (target, source)
140
141 def Builder(**kw):
142     """A factory for builder objects."""
143     composite = None
144     if kw.has_key('generator'):
145         if kw.has_key('action'):
146             raise UserError, "You must not specify both an action and a generator."
147         kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
148         del kw['generator']
149     elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
150         composite = DictCmdGenerator(kw['action'])
151         kw['action'] = SCons.Action.CommandGenerator(composite)
152         kw['src_suffix'] = composite.src_suffixes()
153
154     if kw.has_key('emitter'):
155         emitter = kw['emitter']
156         if SCons.Util.is_String(emitter):
157             # This allows users to pass in an Environment
158             # variable reference (like "$FOO") as an emitter.
159             # We will look in that Environment variable for
160             # a callable to use as the actual emitter.
161             var = SCons.Util.get_environment_var(emitter)
162             if not var:
163                 raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
164             kw['emitter'] = EmitterProxy(var)
165         elif SCons.Util.is_Dict(emitter):
166             kw['emitter'] = DictEmitter(emitter)
167
168     if kw.has_key('src_builder'):
169         ret = apply(MultiStepBuilder, (), kw)
170     else:
171         ret = apply(BuilderBase, (), kw)
172
173     if composite:
174         ret = CompositeBuilder(ret, composite)
175
176     return ret
177
178 def _init_nodes(builder, env, overrides, tlist, slist):
179     """Initialize lists of target and source nodes with all of
180     the proper Builder information.
181     """
182
183     # First, figure out if there are any errors in the way the targets
184     # were specified.
185     for t in tlist:
186         if t.side_effect:
187             raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
188         if t.has_builder():
189             if t.env != env:
190                 raise UserError, "Two different environments were specified for the same target: %s"%str(t)
191             elif t.overrides != overrides:
192                 raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
193             elif builder.scanner and builder.scanner != t.target_scanner:
194                 raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
195
196             if builder.multi:
197                 if t.builder != builder:
198                     if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
199                         raise UserError, "Two different target sets have a target in common: %s"%str(t)
200                     else:
201                         raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t))
202             elif t.sources != slist:
203                 raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
204
205     # The targets are fine, so find or make the appropriate Executor to
206     # build this particular list of targets from this particular list of
207     # sources.
208     executor = None
209     if builder.multi:
210         try:
211             executor = tlist[0].get_executor(create = 0)
212         except AttributeError:
213             pass
214         else:
215             executor.add_sources(slist)
216     if executor is None:
217         executor = SCons.Executor.Executor(builder,
218                                            tlist[0].generate_build_env(env),
219                                            overrides,
220                                            tlist,
221                                            slist)
222
223     # Now set up the relevant information in the target Nodes themselves.
224     for t in tlist:
225         t.overrides = overrides
226         t.cwd = SCons.Node.FS.default_fs.getcwd()
227         t.builder_set(builder)
228         t.env_set(env)
229         t.add_source(slist)
230         t.set_executor(executor)
231         if builder.scanner:
232             t.target_scanner = builder.scanner
233
234     # Last, add scanners from the Environment to the source Nodes.
235     for s in slist:
236         src_key = s.scanner_key()        # the file suffix
237         scanner = env.get_scanner(src_key)
238         if scanner:
239             s.source_scanner = scanner
240
241 class EmitterProxy:
242     """This is a callable class that can act as a
243     Builder emitter.  It holds on to a string that
244     is a key into an Environment dictionary, and will
245     look there at actual build time to see if it holds
246     a callable.  If so, we will call that as the actual
247     emitter."""
248     def __init__(self, var):
249         self.var = SCons.Util.to_String(var)
250
251     def __call__(self, target, source, env):
252         emitter = self.var
253
254         # Recursively substitute the variable.
255         # We can't use env.subst() because it deals only
256         # in strings.  Maybe we should change that?
257         while SCons.Util.is_String(emitter) and \
258               env.has_key(emitter):
259             emitter = env[emitter]
260         if not callable(emitter):
261             return (target, source)
262
263         return emitter(target, source, env)
264
265     def __cmp__(self, other):
266         return cmp(self.var, other.var)
267
268 class BuilderBase:
269     """Base class for Builders, objects that create output
270     nodes (files) from input nodes (files).
271     """
272
273     def __init__(self,  action = None,
274                         prefix = '',
275                         suffix = '',
276                         src_suffix = '',
277                         node_factory = SCons.Node.FS.default_fs.File,
278                         target_factory = None,
279                         source_factory = None,
280                         scanner = None,
281                         emitter = None,
282                         multi = 0,
283                         env = None,
284                         overrides = {}):
285         self.action = SCons.Action.Action(action)
286         self.multi = multi
287         self.prefix = prefix
288         self.suffix = suffix
289         self.env = env
290         self.overrides = overrides
291
292         self.set_src_suffix(src_suffix)
293
294         self.target_factory = target_factory or node_factory
295         self.source_factory = source_factory or node_factory
296         self.scanner = scanner
297
298         self.emitter = emitter
299
300     def __nonzero__(self):
301         raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
302
303     def get_name(self, env):
304         """Attempts to get the name of the Builder.
305
306         Look at the BUILDERS variable of env, expecting it to be a
307         dictionary containing this Builder, and return the key of the
308         dictionary."""
309
310         try:
311             index = env['BUILDERS'].values().index(self)
312             return env['BUILDERS'].keys()[index]
313         except (AttributeError, KeyError, ValueError):
314             return str(self.__class__)
315
316     def __cmp__(self, other):
317         return cmp(self.__dict__, other.__dict__)
318
319     def splitext(self, path):
320         return SCons.Util.splitext(path)
321
322     def _create_nodes(self, env, overrides, target = None, source = None):
323         """Create and return lists of target and source nodes.
324         """
325         def adjustixes(files, pre, suf, self=self):
326             if not files:
327                 return []
328             ret = []
329             if not SCons.Util.is_List(files):
330                 files = [files]
331
332             for f in files:
333                 if SCons.Util.is_String(f):
334                     if pre:
335                         path, fn = os.path.split(os.path.normpath(f))
336                         if fn[:len(pre)] != pre:
337                             f = os.path.join(path, pre + fn)
338                     # Only append a suffix if the file does not have one.
339                     if suf and not self.splitext(f)[1]:
340                         if f[-len(suf):] != suf:
341                             f = f + suf
342                 ret.append(f)
343             return ret
344
345         env = env.Override(overrides)
346
347         pre = self.get_prefix(env)
348         suf = self.get_suffix(env)
349         src_suf = self.get_src_suffix(env)
350
351         source = adjustixes(source, None, src_suf)
352         slist = SCons.Node.arg2nodes(source, self.source_factory)
353
354         if target is None:
355             try:
356                 t_from_s = slist[0].target_from_source
357             except AttributeError:
358                 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
359             tlist = [ t_from_s(pre, suf, self.splitext) ]
360         else:
361             target = adjustixes(target, pre, suf)
362             tlist = SCons.Node.arg2nodes(target, self.target_factory)
363
364         if self.emitter:
365             # The emitter is going to do str(node), but because we're
366             # being called *from* a builder invocation, the new targets
367             # don't yet have a builder set on them and will look like
368             # source files.  Fool the emitter's str() calls by setting
369             # up a temporary builder on the new targets.
370             new_targets = []
371             for t in tlist:
372                 if not t.is_derived():
373                     t.builder = self
374                     new_targets.append(t)
375         
376             target, source = self.emitter(target=tlist, source=slist, env=env)
377
378             # Now delete the temporary builders that we attached to any
379             # new targets, so that _init_nodes() doesn't do weird stuff
380             # to them because it thinks they already have builders.
381             for t in new_targets:
382                 if t.builder is self:
383                     # Only delete the temporary builder if the emitter
384                     # didn't change it on us.
385                     t.builder = None
386
387             # Have to call arg2nodes yet again, since it is legal for
388             # emitters to spit out strings as well as Node instances.
389             slist = SCons.Node.arg2nodes(source, self.source_factory)
390             tlist = SCons.Node.arg2nodes(target, self.target_factory)
391
392         return tlist, slist
393
394     def __call__(self, env, target = None, source = _null, **overrides):
395         if source is _null:
396             source = target
397             target = None
398         tlist, slist = self._create_nodes(env, overrides, target, source)
399
400         if len(tlist) == 1:
401             _init_nodes(self, env, overrides, tlist, slist)
402             tlist = tlist[0]
403         else:
404             _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist)
405
406         return tlist
407
408     def adjust_suffix(self, suff):
409         if suff and not suff[0] in [ '.', '$' ]:
410             return '.' + suff
411         return suff
412
413     def get_prefix(self, env):
414         prefix = self.prefix
415         if callable(prefix):
416             prefix = prefix(env)
417         return env.subst(prefix)
418
419     def get_suffix(self, env):
420         suffix = self.suffix
421         if callable(suffix):
422             suffix = suffix(env)
423         else:
424             suffix = self.adjust_suffix(suffix)
425         return env.subst(suffix)
426
427     def src_suffixes(self, env):
428         return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)),
429                    self.src_suffix)
430
431     def set_src_suffix(self, src_suffix):
432         if not src_suffix:
433             src_suffix = []
434         elif not SCons.Util.is_List(src_suffix):
435             src_suffix = [ src_suffix ]
436         self.src_suffix = src_suffix
437
438     def get_src_suffix(self, env):
439         """Get the first src_suffix in the list of src_suffixes."""
440         ret = self.src_suffixes(env)
441         if not ret:
442             return ''
443         return ret[0]
444
445     def targets(self, node):
446         """Return the list of targets for this builder instance.
447
448         For most normal builders, this is just the supplied node.
449         """
450         return [ node ]
451
452     def add_emitter(self, suffix, emitter):
453         """Add a suffix-emitter mapping to this Builder.
454
455         This assumes that emitter has been initialized with an
456         appropriate dictionary type, and will throw a TypeError if
457         not, so the caller is responsible for knowing that this is an
458         appropriate method to call for the Builder in question.
459         """
460         self.emitter[suffix] = emitter
461
462 class ListBuilder(SCons.Util.Proxy):
463     """A Proxy to support building an array of targets (for example,
464     foo.o and foo.h from foo.y) from a single Action execution.
465     """
466
467     def __init__(self, builder, env, tlist):
468         SCons.Util.Proxy.__init__(self, builder)
469         self.builder = builder
470         self.scanner = builder.scanner
471         self.env = env
472         self.tlist = tlist
473         self.multi = builder.multi
474
475     def targets(self, node):
476         """Return the list of targets for this builder instance.
477         """
478         return self.tlist
479
480     def __cmp__(self, other):
481         return cmp(self.__dict__, other.__dict__)
482
483     def get_name(self, env):
484         """Attempts to get the name of the Builder."""
485
486         return "ListBuilder(%s)" % self.builder.get_name(env)
487
488 class MultiStepBuilder(BuilderBase):
489     """This is a builder subclass that can build targets in
490     multiple steps.  The src_builder parameter to the constructor
491     accepts a builder that is called to build sources supplied to
492     this builder.  The targets of that first build then become
493     the sources of this builder.
494
495     If this builder has a src_suffix supplied, then the src_builder
496     builder is NOT invoked if the suffix of a source file matches
497     src_suffix.
498     """
499     def __init__(self,  src_builder,
500                         action = None,
501                         prefix = '',
502                         suffix = '',
503                         src_suffix = '',
504                         node_factory = SCons.Node.FS.default_fs.File,
505                         target_factory = None,
506                         source_factory = None,
507                         scanner=None,
508                         emitter=None):
509         BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
510                              node_factory, target_factory, source_factory,
511                              scanner, emitter)
512         if not SCons.Util.is_List(src_builder):
513             src_builder = [ src_builder ]
514         self.src_builder = src_builder
515         self.sdict = {}
516         self.cached_src_suffixes = {} # source suffixes keyed on id(env)
517
518     def __call__(self, env, target = None, source = _null, **kw):
519         if source is _null:
520             source = target
521             target = None
522
523         slist = SCons.Node.arg2nodes(source, self.source_factory)
524         final_sources = []
525
526         try:
527             sdict = self.sdict[id(env)]
528         except KeyError:
529             sdict = {}
530             self.sdict[id(env)] = sdict
531             for bld in self.src_builder:
532                 if SCons.Util.is_String(bld):
533                     try:
534                         bld = env['BUILDERS'][bld]
535                     except KeyError:
536                         continue
537                 for suf in bld.src_suffixes(env):
538                     sdict[suf] = bld
539
540         src_suffixes = self.src_suffixes(env)
541
542         for snode in slist:
543             path, ext = self.splitext(snode.get_abspath())
544             if sdict.has_key(ext):
545                 src_bld = sdict[ext]
546                 tgt = apply(src_bld, (env, path, snode), kw)
547                 # Only supply the builder with sources it is capable
548                 # of building.
549                 if SCons.Util.is_List(tgt):
550                     tgt = filter(lambda x, self=self, suf=src_suffixes:
551                                  self.splitext(SCons.Util.to_String(x))[1] in suf,
552                                  tgt)
553                 if not SCons.Util.is_List(tgt):
554                     final_sources.append(tgt)
555                 else:
556                     final_sources.extend(tgt)
557             else:
558                 final_sources.append(snode)
559
560         return apply(BuilderBase.__call__,
561                      (self, env, target, final_sources), kw)
562
563     def get_src_builders(self, env):
564         """Return all the src_builders for this Builder.
565
566         This is essentially a recursive descent of the src_builder "tree."
567         """
568         ret = []
569         for bld in self.src_builder:
570             if SCons.Util.is_String(bld):
571                 # All Environments should have a BUILDERS
572                 # variable, so no need to check for it.
573                 try:
574                     bld = env['BUILDERS'][bld]
575                 except KeyError:
576                     continue
577             ret.append(bld)
578         return ret
579
580     def src_suffixes(self, env):
581         """Return a list of the src_suffix attributes for all
582         src_builders of this Builder.
583         """
584         try:
585             return self.cached_src_suffixes[id(env)]
586         except KeyError:
587             suffixes = BuilderBase.src_suffixes(self, env)
588             for builder in self.get_src_builders(env):
589                 suffixes.extend(builder.src_suffixes(env))
590             self.cached_src_suffixes[id(env)] = suffixes
591             return suffixes
592
593 class CompositeBuilder(SCons.Util.Proxy):
594     """A Builder Proxy whose main purpose is to always have
595     a DictCmdGenerator as its action, and to provide access
596     to the DictCmdGenerator's add_action() method.
597     """
598
599     def __init__(self, builder, cmdgen):
600         SCons.Util.Proxy.__init__(self, builder)
601
602         # cmdgen should always be an instance of DictCmdGenerator.
603         self.cmdgen = cmdgen
604         self.builder = builder
605
606     def add_action(self, suffix, action):
607         self.cmdgen.add_action(suffix, action)
608         self.set_src_suffix(self.cmdgen.src_suffixes())
609         
610     def __cmp__(self, other):
611         return cmp(self.__dict__, other.__dict__)