27
117
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
__revision__ = "src/engine/SCons/Builder.py 0.D006 2002/03/28 02:47:47 software"
36
from Errors import UserError
120
__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Builder.py 0.96.1.D001 2004/08/23 09:55:29 knight"
38
125
import SCons.Action
126
from SCons.Debug import logInstanceCreation
127
from SCons.Errors import InternalError, UserError
128
import SCons.Executor
40
129
import SCons.Node.FS
131
import SCons.Warnings
138
class DictCmdGenerator(SCons.Util.Selector):
139
"""This is a callable class that can be used as a
140
command generator function. It holds on to a dictionary
141
mapping file suffixes to Actions. It uses that dictionary
142
to return the proper action based on the file suffix of
145
def src_suffixes(self):
148
def add_action(self, suffix, action):
149
"""Add a suffix-action pair to the mapping.
151
self[suffix] = action
153
def __call__(self, target, source, env, for_signature):
155
for src in map(str, source):
156
my_ext = SCons.Util.splitext(src)[1]
157
if ext and my_ext != ext:
158
raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
162
raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
165
ret = SCons.Util.Selector.__call__(self, env, source)
167
raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
169
raise UserError("While building `%s': Don't know how to build a file with suffix `%s'." % (repr(map(str, target)), ext))
172
class CallableSelector(SCons.Util.Selector):
173
"""A callable dictionary that will, in turn, call the value it
175
def __call__(self, env, source):
176
value = SCons.Util.Selector.__call__(self, env, source)
178
value = value(env, source)
181
class DictEmitter(SCons.Util.Selector):
182
"""A callable dictionary that maps file suffixes to emitters.
183
When called, it finds the right emitter in its dictionary for the
184
suffix of the first source file, and calls that emitter to get the
185
right lists of targets and sources to return. If there's no emitter
186
for the suffix in its dictionary, the original target and source are
189
def __call__(self, target, source, env):
190
emitter = SCons.Util.Selector.__call__(self, env, source)
192
target, source = emitter(target, source, env)
193
return (target, source)
195
class ListEmitter(UserList.UserList):
196
"""A callable list of emitters that calls each in sequence,
197
returning the result.
199
def __call__(self, target, source, env):
201
target, source = e(target, source, env)
202
return (target, source)
204
# These are a common errors when calling a Builder;
205
# they are similar to the 'target' and 'source' keyword args to builders,
206
# so we issue warnings when we see them. The warnings can, of course,
208
misleading_keywords = {
209
'targets' : 'target',
210
'sources' : 'source',
213
class OverrideWarner(UserDict.UserDict):
214
"""A class for warning about keyword arguments that we use as
215
overrides in a Builder call.
217
This class exists to handle the fact that a single MultiStepBuilder
218
call can actually invoke multiple builders as a result of a single
219
user-level Builder call. This class only emits the warnings once,
220
no matter how many Builders are invoked.
222
def __init__(self, dict):
223
UserDict.UserDict.__init__(self, dict)
224
self.already_warned = None
226
if self.already_warned:
228
for k in self.keys():
230
alt = misleading_keywords[k]
234
SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning,
235
"Did you mean to use `%s' instead of `%s'?" % (alt, k))
236
self.already_warned = 1
45
238
def Builder(**kw):
46
239
"""A factory for builder objects."""
48
241
if kw.has_key('generator'):
49
242
if kw.has_key('action'):
50
243
raise UserError, "You must not specify both an action and a generator."
51
244
kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
52
245
del kw['generator']
54
if kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
55
return apply(CompositeBuilder, (), kw)
56
elif kw.has_key('src_builder'):
57
return apply(MultiStepBuilder, (), kw)
246
elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
247
composite = DictCmdGenerator(kw['action'])
248
kw['action'] = SCons.Action.CommandGenerator(composite)
249
kw['src_suffix'] = composite.src_suffixes()
251
if kw.has_key('emitter'):
252
emitter = kw['emitter']
253
if SCons.Util.is_String(emitter):
254
# This allows users to pass in an Environment
255
# variable reference (like "$FOO") as an emitter.
256
# We will look in that Environment variable for
257
# a callable to use as the actual emitter.
258
var = SCons.Util.get_environment_var(emitter)
260
raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
261
kw['emitter'] = EmitterProxy(var)
262
elif SCons.Util.is_Dict(emitter):
263
kw['emitter'] = DictEmitter(emitter)
264
elif SCons.Util.is_List(emitter):
265
kw['emitter'] = ListEmitter(emitter)
267
if kw.has_key('src_builder'):
268
ret = apply(MultiStepBuilder, (), kw)
59
return apply(BuilderBase, (), kw)
63
def _init_nodes(builder, env, tlist, slist):
270
ret = apply(BuilderBase, (), kw)
272
if not composite is None:
273
ret = CompositeBuilder(ret, composite)
277
def _init_nodes(builder, env, overrides, tlist, slist):
64
278
"""Initialize lists of target and source nodes with all of
65
279
the proper Builder information.
69
src_key = slist[0].scanner_key() # the file suffix
70
src_scanner = env.get_scanner(src_key)
72
src_scanner = src_scanner.instance(env)
75
t.cwd = SCons.Node.FS.default_fs.getcwd() # XXX
282
# First, figure out if there are any errors in the way the targets
286
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
288
if not t.env is None and not t.env is env:
289
t_contents = t.builder.action.get_contents(tlist, slist, t.env)
290
contents = t.builder.action.get_contents(tlist, slist, env)
292
if t_contents == contents:
293
SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning,
294
"Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), t.builder.action.strfunction(tlist, slist, t.env)))
297
raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t)
299
elif t.overrides != overrides:
300
raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
302
elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner:
303
raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
306
if t.builder != builder:
307
if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
308
raise UserError, "Two different target sets have a target in common: %s"%str(t)
310
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))
311
elif t.sources != slist:
312
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
314
if builder.single_source:
316
raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
318
# The targets are fine, so find or make the appropriate Executor to
319
# build this particular list of targets from this particular list of
324
executor = tlist[0].get_executor(create = 0)
325
except AttributeError:
328
executor.add_sources(slist)
330
executor = SCons.Executor.Executor(builder.action,
332
[builder.overrides, overrides],
336
# Now set up the relevant information in the target Nodes themselves.
338
t.overrides = overrides
339
t.cwd = SCons.Node.FS.default_fs.getcwd()
76
340
t.builder_set(builder)
78
342
t.add_source(slist)
80
t.scanner_set(builder.scanner.instance(env))
82
t.src_scanner_set(src_key, src_scanner)
343
t.set_executor(executor)
344
if builder.target_scanner:
345
t.target_scanner = builder.target_scanner
346
if t.source_scanner is None:
347
t.source_scanner = builder.source_scanner
349
# Add backup source scanners from the environment to the source
350
# nodes. This may not be necessary if the node will have a real
351
# source scanner added later (which is why these are the "backup"
352
# source scanners, not the real ones), but because source nodes may
353
# be used multiple times for different targets, it ends up being
354
# more efficient to do this calculation once here, as opposed to
355
# delaying it until later when we potentially have to calculate it
356
# over and over and over.
358
if s.source_scanner is None and s.backup_source_scanner is None:
359
s.backup_source_scanner = env.get_scanner(s.scanner_key())
362
"""This is a callable class that can act as a
363
Builder emitter. It holds on to a string that
364
is a key into an Environment dictionary, and will
365
look there at actual build time to see if it holds
366
a callable. If so, we will call that as the actual
368
def __init__(self, var):
369
self.var = SCons.Util.to_String(var)
371
def __call__(self, target, source, env):
374
# Recursively substitute the variable.
375
# We can't use env.subst() because it deals only
376
# in strings. Maybe we should change that?
377
while SCons.Util.is_String(emitter) and env.has_key(emitter):
378
emitter = env[emitter]
379
if callable(emitter):
380
target, source = emitter(target, source, env)
381
elif SCons.Util.is_List(emitter):
383
target, source = e(target, source, env)
385
return (target, source)
388
def __cmp__(self, other):
389
return cmp(self.var, other.var)
86
391
class BuilderBase:
87
392
"""Base class for Builders, objects that create output
88
393
nodes (files) from input nodes (files).
91
def __init__(self, name = None,
96
node_factory = SCons.Node.FS.default_fs.File,
97
target_factory = None,
98
source_factory = None,
101
raise UserError, "You must specify a name for the builder."
103
self.action = SCons.Action.Action(action)
107
self.src_suffix = src_suffix
108
self.target_factory = target_factory or node_factory
109
self.source_factory = source_factory or node_factory
110
self.scanner = scanner
111
if self.suffix and self.suffix[0] not in '.$':
112
self.suffix = '.' + self.suffix
113
if self.src_suffix and self.src_suffix[0] not in '.$':
114
self.src_suffix = '.' + self.src_suffix
396
def __init__(self, action = None,
400
target_factory = SCons.Node.FS.default_fs.File,
401
source_factory = SCons.Node.FS.default_fs.File,
402
target_scanner = None,
403
source_scanner = None,
409
if __debug__: logInstanceCreation(self, 'BuilderBase')
410
self.action = SCons.Action.Action(action)
412
if SCons.Util.is_Dict(prefix):
413
prefix = CallableSelector(prefix)
415
if SCons.Util.is_Dict(suffix):
416
suffix = CallableSelector(suffix)
419
self.single_source = single_source
420
if overrides.has_key('overrides'):
421
SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
422
"The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
423
"\tspecify the items as keyword arguments to the Builder() call instead.")
424
overrides.update(overrides['overrides'])
425
del overrides['overrides']
426
self.overrides = overrides
428
self.set_src_suffix(src_suffix)
430
self.target_factory = target_factory
431
self.source_factory = source_factory
432
self.target_scanner = target_scanner
433
self.source_scanner = source_scanner
435
self.emitter = emitter
437
def __nonzero__(self):
438
raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
440
def get_name(self, env):
441
"""Attempts to get the name of the Builder.
443
Look at the BUILDERS variable of env, expecting it to be a
444
dictionary containing this Builder, and return the key of the
448
index = env['BUILDERS'].values().index(self)
449
return env['BUILDERS'].keys()[index]
450
except (AttributeError, KeyError, ValueError):
451
return str(self.__class__)
116
453
def __cmp__(self, other):
117
return cmp(self.__dict__, other.__dict__)
119
def _create_nodes(self, env, target = None, source = None):
454
return cmp(self.__dict__, other.__dict__)
456
def splitext(self, path):
457
return SCons.Util.splitext(path)
459
def _create_nodes(self, env, overwarn, target = None, source = None):
120
460
"""Create and return lists of target and source nodes.
122
def adjustixes(files, pre, suf):
124
if SCons.Util.is_String(files):
125
files = string.split(files)
462
def _adjustixes(files, pre, suf):
126
466
if not SCons.Util.is_List(files):
129
470
if SCons.Util.is_String(f):
130
if pre and f[:len(pre)] != pre:
131
path, fn = os.path.split(os.path.normpath(f))
132
f = os.path.join(path, pre + fn)
134
if f[-len(suf):] != suf:
139
tlist = SCons.Node.arg2nodes(adjustixes(target,
140
env.subst(self.prefix),
141
env.subst(self.suffix)),
144
slist = SCons.Node.arg2nodes(adjustixes(source,
146
env.subst(self.src_suffix)),
471
f = SCons.Util.adjustixes(f, pre, suf)
477
env = env.Override(overwarn.data)
479
src_suf = self.get_src_suffix(env)
481
source = _adjustixes(source, None, src_suf)
482
slist = env.arg2nodes(source, self.source_factory)
484
pre = self.get_prefix(env, slist)
485
suf = self.get_suffix(env, slist)
489
t_from_s = slist[0].target_from_source
490
except AttributeError:
491
raise UserError("Do not know how to create a target from source `%s'" % slist[0])
492
tlist = [ t_from_s(pre, suf, self.splitext) ]
494
target = _adjustixes(target, pre, suf)
495
tlist = env.arg2nodes(target, self.target_factory)
498
# The emitter is going to do str(node), but because we're
499
# being called *from* a builder invocation, the new targets
500
# don't yet have a builder set on them and will look like
501
# source files. Fool the emitter's str() calls by setting
502
# up a temporary builder on the new targets.
505
if not t.is_derived():
507
new_targets.append(t)
509
target, source = self.emitter(target=tlist, source=slist, env=env)
511
# Now delete the temporary builders that we attached to any
512
# new targets, so that _init_nodes() doesn't do weird stuff
513
# to them because it thinks they already have builders.
514
for t in new_targets:
515
if t.builder is self:
516
# Only delete the temporary builder if the emitter
517
# didn't change it on us.
520
# Have to call arg2nodes yet again, since it is legal for
521
# emitters to spit out strings as well as Node instances.
522
slist = env.arg2nodes(source, self.source_factory)
523
tlist = env.arg2nodes(target, self.target_factory)
148
525
return tlist, slist
150
def __call__(self, env, target = None, source = None):
151
tlist, slist = self._create_nodes(env, target, source)
527
def _execute(self, env, target = None, source = _null, overwarn={}):
532
if(self.single_source and
533
SCons.Util.is_List(source) and
537
if target is None: target = [None]*len(source)
538
for k in range(len(source)):
539
t = self._execute(env, target[k], source[k], overwarn)
540
if SCons.Util.is_List(t):
546
tlist, slist = self._create_nodes(env, overwarn, target, source)
153
548
if len(tlist) == 1:
154
_init_nodes(self, env, tlist, slist)
157
_init_nodes(ListBuilder(self, env, tlist), env, tlist, slist)
551
builder = ListBuilder(self, env, tlist)
552
_init_nodes(builder, env, overwarn.data, tlist, slist)
162
def execute(self, **kw):
163
"""Execute a builder's action to create an output object.
165
return apply(self.action.execute, (), kw)
167
def get_raw_contents(self, **kw):
168
"""Fetch the "contents" of the builder's action.
170
return apply(self.action.get_raw_contents, (), kw)
172
def get_contents(self, **kw):
173
"""Fetch the "contents" of the builder's action
174
(for signature calculation).
176
return apply(self.action.get_contents, (), kw)
556
def __call__(self, env, target = None, source = _null, **kw):
557
return self._execute(env, target, source, OverrideWarner(kw))
559
def adjust_suffix(self, suff):
560
if suff and not suff[0] in [ '.', '_', '$' ]:
564
def get_prefix(self, env, sources=[]):
567
prefix = prefix(env, sources)
568
return env.subst(prefix)
570
def get_suffix(self, env, sources=[]):
573
suffix = suffix(env, sources)
575
suffix = self.adjust_suffix(suffix)
576
return env.subst(suffix)
178
578
def src_suffixes(self, env):
179
if self.src_suffix != '':
180
return [env.subst(self.src_suffix)]
579
return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)),
582
def set_src_suffix(self, src_suffix):
585
elif not SCons.Util.is_List(src_suffix):
586
src_suffix = [ src_suffix ]
587
self.src_suffix = src_suffix
589
def get_src_suffix(self, env):
590
"""Get the first src_suffix in the list of src_suffixes."""
591
ret = self.src_suffixes(env)
183
596
def targets(self, node):
184
597
"""Return the list of targets for this builder instance.
242
653
def __init__(self, src_builder,
248
node_factory = SCons.Node.FS.default_fs.File,
249
target_factory = None,
250
source_factory = None,
252
BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
253
node_factory, target_factory, source_factory,
658
target_factory = SCons.Node.FS.default_fs.File,
659
source_factory = SCons.Node.FS.default_fs.File,
660
target_scanner = None,
661
source_scanner = None,
664
if __debug__: logInstanceCreation(self)
665
BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
666
target_factory, source_factory,
667
target_scanner, source_scanner, emitter,
668
single_source = single_source)
669
if not SCons.Util.is_List(src_builder):
670
src_builder = [ src_builder ]
255
671
self.src_builder = src_builder
257
def __call__(self, env, target = None, source = None):
258
slist = SCons.Node.arg2nodes(source, self.source_factory)
673
self.cached_src_suffixes = {} # source suffixes keyed on id(env)
675
def _execute(self, env, target = None, source = _null, overwarn={}):
680
slist = env.arg2nodes(source, self.source_factory)
259
681
final_sources = []
260
src_suffix = env.subst(self.src_suffix)
262
for suff in self.src_builder.src_suffixes(env):
684
sdict = self.sdict[id(env)]
687
self.sdict[id(env)] = sdict
688
for bld in self.src_builder:
689
if SCons.Util.is_String(bld):
691
bld = env['BUILDERS'][bld]
694
for suf in bld.src_suffixes(env):
697
src_suffixes = self.src_suffixes(env)
264
699
for snode in slist:
265
path, ext = os.path.splitext(snode.abspath)
266
if sdict.has_key(ext):
267
tgt = self.src_builder(env, target = [ path ], source = snode)
268
if not SCons.Util.is_List(tgt):
269
final_sources.append(tgt)
271
final_sources.extend(tgt)
701
get_suffix = snode.get_suffix
702
except AttributeError:
703
ext = self.splitext(str(snode))
707
subsidiary_builder = sdict[ext]
273
709
final_sources.append(snode)
274
return BuilderBase.__call__(self, env, target=target,
275
source=final_sources)
277
def src_suffixes(self, env):
278
return BuilderBase.src_suffixes(self, env) + \
279
self.src_builder.src_suffixes(env)
281
class CompositeBuilder(BuilderBase):
282
"""This is a convenient Builder subclass that can build different
283
files based on their suffixes. For each target, this builder
284
will examine the target's sources. If they are all the same
285
suffix, and that suffix is equal to one of the child builders'
286
src_suffix, then that child builder will be used. Otherwise,
287
UserError is thrown."""
288
def __init__(self, name = None,
293
BuilderBase.__init__(self, name=name, prefix=prefix,
295
if src_builder and not SCons.Util.is_List(src_builder):
296
src_builder = [src_builder]
297
self.src_builder = src_builder
298
self.action_dict = action
302
def __call__(self, env, target = None, source = None):
303
tlist, slist = BuilderBase._create_nodes(self, env,
304
target=target, source=source)
307
if not self.sdict.has_key(r):
310
for suff in self.src_suffixes(env):
311
suff = env.subst(suff)
312
self.sdict[r][suff] = suff
313
self.sbuild[r].extend(filter(lambda x, e=env, s=suff:
314
e.subst(x.suffix) == s,
316
for sb in self.sbuild[r]:
317
suff = env.subst(sb.suffix)
318
for s in sb.src_suffixes(env):
319
self.sdict[r][env.subst(s)] = suff
321
sufflist = map(lambda x, s=self.sdict[r]:
322
s[os.path.splitext(x.path)[1]],
325
for suff in sufflist:
326
if last_suffix and last_suffix != suff:
327
raise UserError, "The builder for %s can only build source files of identical suffixes: %s." % \
329
str(map(lambda t: str(t.path), tlist[0].sources)))
335
'action' : self.action_dict[last_suffix],
336
'src_suffix' : last_suffix,
339
sb = filter(lambda x, e=env, s=last_suffix:
340
e.subst(x.suffix) == s,
343
kw['src_builder'] = sb[0]
344
# XXX We should be able to cache this
345
bld = apply(Builder, (), kw)
347
bld.__call__(env, target = tnode, source = slist)
353
def src_suffixes(self, env):
354
suffixes = map(lambda k, e=env: e.subst(k), self.action_dict.keys()) + \
355
reduce(lambda x, y: x + y,
356
map(lambda b, e=env: b.src_suffixes(e),
711
tgt = subsidiary_builder._execute(env, None, snode, overwarn)
712
# If the subsidiary Builder returned more than one target,
713
# then filter out any sources that this Builder isn't
714
# capable of building.
716
tgt = filter(lambda x, self=self, suf=src_suffixes:
717
self.splitext(SCons.Util.to_String(x))[1] in suf,
719
final_sources.extend(tgt)
721
return BuilderBase._execute(self, env, target, final_sources, overwarn)
723
def get_src_builders(self, env):
724
"""Return all the src_builders for this Builder.
726
This is essentially a recursive descent of the src_builder "tree."
729
for bld in self.src_builder:
730
if SCons.Util.is_String(bld):
731
# All Environments should have a BUILDERS
732
# variable, so no need to check for it.
734
bld = env['BUILDERS'][bld]
740
def src_suffixes(self, env):
741
"""Return a list of the src_suffix attributes for all
742
src_builders of this Builder.
745
return self.cached_src_suffixes[id(env)]
747
suffixes = BuilderBase.src_suffixes(self, env)
748
for builder in self.get_src_builders(env):
749
suffixes.extend(builder.src_suffixes(env))
750
self.cached_src_suffixes[id(env)] = suffixes
753
class CompositeBuilder(SCons.Util.Proxy):
754
"""A Builder Proxy whose main purpose is to always have
755
a DictCmdGenerator as its action, and to provide access
756
to the DictCmdGenerator's add_action() method.
759
def __init__(self, builder, cmdgen):
760
if __debug__: logInstanceCreation(self)
761
SCons.Util.Proxy.__init__(self, builder)
763
# cmdgen should always be an instance of DictCmdGenerator.
765
self.builder = builder
767
def add_action(self, suffix, action):
768
self.cmdgen.add_action(suffix, action)
769
self.set_src_suffix(self.cmdgen.src_suffixes())
771
def __cmp__(self, other):
772
return cmp(self.__dict__, other.__dict__)