8 # Copyright (c) 2001, 2002 Steven Knight
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
37 from SCons.Errors import UserError
44 class DictCmdGenerator:
45 """This is a callable class that can be used as a
46 command generator function. It holds on to a dictionary
47 mapping file suffixes to Actions. It uses that dictionary
48 to return the proper action based on the file suffix of
51 def __init__(self, action_dict):
52 self.action_dict = action_dict
54 def src_suffixes(self):
55 return self.action_dict.keys()
57 def __call__(self, source, target, env, **kw):
59 for src in map(str, source):
60 my_ext = os.path.splitext(src)[1]
61 if ext and my_ext != ext:
62 raise UserError("Cannot build multiple sources with different extensions.")
66 raise UserError("Cannot deduce file extension from source files: %s" % repr(map(str, source)))
68 # XXX Do we need to perform Environment substitution
69 # on the keys of action_dict before looking it up?
70 return self.action_dict[ext]
72 raise UserError("Don't know how to build a file with suffix %s." % ext)
75 """A factory for builder objects."""
76 if kw.has_key('generator'):
77 if kw.has_key('action'):
78 raise UserError, "You must not specify both an action and a generator."
79 kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
81 elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
82 action_dict = kw['action']
83 kw['action'] = SCons.Action.CommandGenerator(DictCmdGenerator(action_dict))
84 kw['src_suffix'] = action_dict.keys()
86 if kw.has_key('emitter') and \
87 SCons.Util.is_String(kw['emitter']):
88 # This allows users to pass in an Environment
89 # variable reference (like "$FOO") as an emitter.
90 # We will look in that Environment variable for
91 # a callable to use as the actual emitter.
92 var = SCons.Util.get_environment_var(kw['emitter'])
94 raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % kw['emitter']
95 kw['emitter'] = EmitterProxy(var)
97 if kw.has_key('src_builder'):
98 return apply(MultiStepBuilder, (), kw)
100 return apply(BuilderBase, (), kw)
102 def _init_nodes(builder, env, tlist, slist):
103 """Initialize lists of target and source nodes with all of
104 the proper Builder information.
107 src_key = s.scanner_key() # the file suffix
108 scanner = env.get_scanner(src_key)
110 s.source_scanner = scanner
113 t.cwd = SCons.Node.FS.default_fs.getcwd() # XXX
114 t.builder_set(builder)
118 t.target_scanner = builder.scanner
120 class _callable_adaptor:
121 """When crteating a Builder, you can pass a string OR
122 a callable in for prefix, suffix, or src_suffix.
123 src_suffix even takes a list!
125 If a string or list is passed, we use this class to
126 adapt it to a callable."""
127 def __init__(self, static):
130 def __call__(self, **kw):
133 def __cmp__(self, other):
134 if isinstance(other, _callable_adaptor):
135 return cmp(self.static, other.static)
138 def _adjust_suffix(suff):
139 if suff and not suff[0] in [ '.', '$' ]:
144 """This is a callable class that can act as a
145 Builder emitter. It holds on to a string that
146 is a key into an Environment dictionary, and will
147 look there at actual build time to see if it holds
148 a callable. If so, we will call that as the actual
150 def __init__(self, var):
151 self.var = SCons.Util.to_String(var)
153 def __call__(self, target, source, env, **kw):
156 # Recursively substitue the variable.
157 # We can't use env.subst() because it deals only
158 # in strings. Maybe we should change that?
159 while SCons.Util.is_String(emitter) and \
160 env.has_key(emitter):
161 emitter = env[emitter]
162 if not callable(emitter):
163 return (target, source)
164 args = { 'target':target,
168 return apply(emitter, (), args)
171 """Base class for Builders, objects that create output
172 nodes (files) from input nodes (files).
175 def __init__(self, name = None,
180 node_factory = SCons.Node.FS.default_fs.File,
181 target_factory = None,
182 source_factory = None,
186 raise UserError, "You must specify a name for the builder."
188 self.action = SCons.Action.Action(action)
193 self.prefix = _callable_adaptor(str(prefix))
198 self.suffix = _callable_adaptor(str(suffix))
200 if callable(src_suffix):
201 self.src_suffix = src_suffix
202 elif SCons.Util.is_String(src_suffix):
203 self.src_suffix = _callable_adaptor([ str(src_suffix) ])
205 self.src_suffix = _callable_adaptor(src_suffix)
207 self.target_factory = target_factory or node_factory
208 self.source_factory = source_factory or node_factory
209 self.scanner = scanner
211 self.emitter = emitter
213 def __cmp__(self, other):
214 return cmp(self.__dict__, other.__dict__)
216 def _create_nodes(self, env, args, target = None, source = None):
217 """Create and return lists of target and source nodes.
219 def adjustixes(files, pre, suf):
221 files = SCons.Util.argmunge(files)
224 if SCons.Util.is_String(f):
225 if pre and f[:len(pre)] != pre:
226 path, fn = os.path.split(os.path.normpath(f))
227 f = os.path.join(path, pre + fn)
228 # Only append a suffix if the file does not have one.
229 if suf and not os.path.splitext(f)[1]:
230 if f[-len(suf):] != suf:
235 pre = self.get_prefix(env, args)
236 suf = self.get_suffix(env, args)
237 tlist = SCons.Node.arg2nodes(adjustixes(target,
240 src_suf = self.get_src_suffix(env, args)
241 slist = SCons.Node.arg2nodes(adjustixes(source,
246 emit_args = { 'target' : tlist,
249 emit_args.update(args)
250 target, source = apply(self.emitter, (), emit_args)
252 # Have to run it through again in case the
253 # function returns non-Node targets/sources.
254 tlist = SCons.Node.arg2nodes(adjustixes(target,
257 slist = SCons.Node.arg2nodes(adjustixes(source,
266 def __call__(self, env, target = None, source = None, **kw):
267 tlist, slist = self._create_nodes(env, kw, target, source)
270 _init_nodes(self, env, tlist, slist)
273 _init_nodes(ListBuilder(self, env, tlist), env, tlist, slist)
278 def execute(self, **kw):
279 """Execute a builder's action to create an output object.
281 return apply(self.action.execute, (), kw)
283 def get_raw_contents(self, **kw):
284 """Fetch the "contents" of the builder's action.
286 return apply(self.action.get_raw_contents, (), kw)
288 def get_contents(self, **kw):
289 """Fetch the "contents" of the builder's action
290 (for signature calculation).
292 return apply(self.action.get_contents, (), kw)
294 def src_suffixes(self, env, args):
295 return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
296 apply(self.src_suffix, (), args))
298 def get_src_suffix(self, env, args):
299 """Get the first src_suffix in the list of src_suffixes."""
300 ret = self.src_suffixes(env, args)
306 def get_suffix(self, env, args):
307 return env.subst(_adjust_suffix(apply(self.suffix, (), args)))
309 def get_prefix(self, env, args):
310 return env.subst(apply(self.prefix, (), args))
312 def targets(self, node):
313 """Return the list of targets for this builder instance.
315 For most normal builders, this is just the supplied node.
320 """This is technically not a Builder object, but a wrapper
321 around another Builder object. This is designed to look
322 like a Builder object, though, for purposes of building an
323 array of targets from a single Action execution.
326 def __init__(self, builder, env, tlist):
327 self.builder = builder
328 self.scanner = builder.scanner
332 def execute(self, **kw):
333 if hasattr(self, 'status'):
336 # unlink all targets and make all directories
337 # before building anything
339 kw['target'] = self.tlist
340 self.status = apply(self.builder.execute, (), kw)
342 if not t is kw['target']:
346 def get_raw_contents(self, **kw):
347 return apply(self.builder.get_raw_contents, (), kw)
349 def get_contents(self, **kw):
350 return apply(self.builder.get_contents, (), kw)
352 def src_suffixes(self, env, args):
353 return self.builder.src_suffixes(env, args)
355 def targets(self, node):
356 """Return the list of targets for this builder instance.
360 class MultiStepBuilder(BuilderBase):
361 """This is a builder subclass that can build targets in
362 multiple steps. The src_builder parameter to the constructor
363 accepts a builder that is called to build sources supplied to
364 this builder. The targets of that first build then become
365 the sources of this builder.
367 If this builder has a src_suffix supplied, then the src_builder
368 builder is NOT invoked if the suffix of a source file matches
371 def __init__(self, src_builder,
377 node_factory = SCons.Node.FS.default_fs.File,
378 target_factory = None,
379 source_factory = None,
382 BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
383 node_factory, target_factory, source_factory,
385 if not SCons.Util.is_List(src_builder):
386 src_builder = [ src_builder ]
387 self.src_builder = src_builder
390 def __call__(self, env, target = None, source = None, **kw):
391 slist = SCons.Node.arg2nodes(source, self.source_factory)
396 sdict = self.sdict[r]
399 self.sdict[r] = sdict
400 for bld in self.src_builder:
401 for suf in bld.src_suffixes(env, kw):
405 path, ext = os.path.splitext(snode.abspath)
406 if sdict.has_key(ext):
409 dictArgs = copy.copy(kw)
410 dictArgs['target'] = [path]
411 dictArgs['source'] = snode
412 dictArgs['env'] = env
413 tgt = apply(src_bld, (), dictArgs)
414 if not SCons.Util.is_List(tgt):
417 # Only supply the builder with sources it is capable
419 tgt = filter(lambda x,
420 suf=self.src_suffixes(env, kw):
421 os.path.splitext(SCons.Util.to_String(x))[1] in \
423 final_sources.extend(tgt)
425 final_sources.append(snode)
427 dictKwArgs['target'] = target
428 dictKwArgs['source'] = final_sources
429 return apply(BuilderBase.__call__,
430 (self, env), dictKwArgs)
432 def src_suffixes(self, env, args):
433 return BuilderBase.src_suffixes(self, env, args) + \
434 reduce(lambda x, y: x + y,
435 map(lambda b, e=env, args=args: b.src_suffixes(e, args),