~ubuntu-branches/ubuntu/utopic/scons/utopic-proposed

« back to all changes in this revision

Viewing changes to engine/SCons/Builder.py

  • Committer: Bazaar Package Importer
  • Author(s): Mark Brown
  • Date: 2004-08-24 08:57:22 UTC
  • mfrom: (0.2.1 upstream) (1.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040824085722-hfk4f0pjbyu0ebxv
Tags: 0.96.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
"""SCons.Builder
2
2
 
3
 
XXX
 
3
Builder object subsystem.
 
4
 
 
5
A Builder object is a callable that encapsulates information about how
 
6
to execute actions to create a target Node (file) from source Nodes
 
7
(files), and how to create those dependencies for tracking.
 
8
 
 
9
The main entry point here is the Builder() factory method.  This provides
 
10
a procedural interface that creates the right underlying Builder object
 
11
based on the keyword arguments supplied and the types of the arguments.
 
12
 
 
13
The goal is for this external interface to be simple enough that the
 
14
vast majority of users can create new Builders as necessary to support
 
15
building new types of files in their configurations, without having to
 
16
dive any deeper into this subsystem.
 
17
 
 
18
The base class here is BuilderBase.  This is a concrete base class which
 
19
does, in fact, represent most Builder objects that we (or users) create.
 
20
 
 
21
There is (at present) one subclasses:
 
22
 
 
23
    MultiStepBuilder
 
24
 
 
25
        This is a Builder that knows how to "chain" Builders so that
 
26
        users can specify a source file that requires multiple steps
 
27
        to turn into a target file.  A canonical example is building a
 
28
        program from yacc input file, which requires invoking a builder
 
29
        to turn the .y into a .c, the .c into a .o, and the .o into an
 
30
        executable program.
 
31
 
 
32
There is also two proxies that look like Builders:
 
33
 
 
34
    CompositeBuilder
 
35
 
 
36
        This proxies for a Builder with an action that is actually a
 
37
        dictionary that knows how to map file suffixes to a specific
 
38
        action.  This is so that we can invoke different actions
 
39
        (compilers, compile options) for different flavors of source
 
40
        files.
 
41
 
 
42
    ListBuilder
 
43
 
 
44
        This proxies for a Builder *invocation* where the target
 
45
        is a list of files, not a single file.
 
46
 
 
47
Builders and their proxies have the following public interface methods
 
48
used by other modules:
 
49
 
 
50
    __call__()
 
51
        THE public interface.  Calling a Builder object (with the
 
52
        use of internal helper methods) sets up the target and source
 
53
        dependencies, appropriate mapping to a specific action, and the
 
54
        environment manipulation necessary for overridden construction
 
55
        variable.  This also takes care of warning about possible mistakes
 
56
        in keyword arguments.
 
57
 
 
58
    targets()
 
59
        Returns the list of targets for a specific builder instance.
 
60
 
 
61
    add_emitter()
 
62
        Adds an emitter for a specific file suffix, used by some Tool
 
63
        modules to specify that (for example) a yacc invocation on a .y
 
64
        can create a .h *and* a .c file.
 
65
 
 
66
    add_action()
 
67
        Adds an action for a specific file suffix, heavily used by
 
68
        Tool modules to add their specific action(s) for turning
 
69
        a source file into an object file to the global static
 
70
        and shared object file Builders.
 
71
 
 
72
There are the following methods for internal use within this module:
 
73
 
 
74
    _execute()
 
75
        The internal method that handles the heavily lifting when a
 
76
        Builder is called.  This is used so that the __call__() methods
 
77
        can set up warning about possible mistakes in keyword-argument
 
78
        overrides, and *then* execute all of the steps necessary so that
 
79
        the warnings only occur once.
 
80
 
 
81
    get_name()
 
82
        Returns the Builder's name within a specific Environment,
 
83
        primarily used to try to return helpful information in error
 
84
        messages.
 
85
 
 
86
    adjust_suffix()
 
87
    get_prefix()
 
88
    get_suffix()
 
89
    get_src_suffix()
 
90
    set_src_suffix()
 
91
        Miscellaneous stuff for handling the prefix and suffix
 
92
        manipulation we use in turning source file names into target
 
93
        file names.
4
94
 
5
95
"""
6
96
 
7
97
#
8
 
# Copyright (c) 2001, 2002 Steven Knight
 
98
# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
9
99
#
10
100
# Permission is hereby granted, free of charge, to any person obtaining
11
101
# a copy of this software and associated documentation files (the
27
117
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
118
#
29
119
 
30
 
__revision__ = "src/engine/SCons/Builder.py 0.D006 2002/03/28 02:47:47 software"
31
 
 
32
 
 
33
 
 
34
 
import os.path
35
 
import string
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"
 
121
 
 
122
import UserDict
 
123
import UserList
37
124
 
38
125
import SCons.Action
39
 
import SCons.Node
 
126
from SCons.Debug import logInstanceCreation
 
127
from SCons.Errors import InternalError, UserError
 
128
import SCons.Executor
40
129
import SCons.Node.FS
41
130
import SCons.Util
42
 
 
43
 
 
 
131
import SCons.Warnings
 
132
 
 
133
class _Null:
 
134
    pass
 
135
 
 
136
_null = _Null
 
137
 
 
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
 
143
    the source file."""
 
144
 
 
145
    def src_suffixes(self):
 
146
        return self.keys()
 
147
 
 
148
    def add_action(self, suffix, action):
 
149
        """Add a suffix-action pair to the mapping.
 
150
        """
 
151
        self[suffix] = action
 
152
 
 
153
    def __call__(self, target, source, env, for_signature):
 
154
        ext = None
 
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))
 
159
            ext = my_ext
 
160
 
 
161
        if not ext:
 
162
            raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
 
163
 
 
164
        try:
 
165
            ret = SCons.Util.Selector.__call__(self, env, source)
 
166
        except KeyError, e:
 
167
            raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
 
168
        if ret is None:
 
169
            raise UserError("While building `%s': Don't know how to build a file with suffix `%s'." % (repr(map(str, target)), ext))
 
170
        return ret
 
171
 
 
172
class CallableSelector(SCons.Util.Selector):
 
173
    """A callable dictionary that will, in turn, call the value it
 
174
    finds if it can."""
 
175
    def __call__(self, env, source):
 
176
        value = SCons.Util.Selector.__call__(self, env, source)
 
177
        if callable(value):
 
178
            value = value(env, source)
 
179
        return value
 
180
 
 
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
 
187
    returned.
 
188
    """
 
189
    def __call__(self, target, source, env):
 
190
        emitter = SCons.Util.Selector.__call__(self, env, source)
 
191
        if emitter:
 
192
            target, source = emitter(target, source, env)
 
193
        return (target, source)
 
194
 
 
195
class ListEmitter(UserList.UserList):
 
196
    """A callable list of emitters that calls each in sequence,
 
197
    returning the result.
 
198
    """
 
199
    def __call__(self, target, source, env):
 
200
        for e in self.data:
 
201
            target, source = e(target, source, env)
 
202
        return (target, source)
 
203
 
 
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,
 
207
# be disabled.
 
208
misleading_keywords = {
 
209
    'targets'   : 'target',
 
210
    'sources'   : 'source',
 
211
}
 
212
 
 
213
class OverrideWarner(UserDict.UserDict):
 
214
    """A class for warning about keyword arguments that we use as
 
215
    overrides in a Builder call.
 
216
 
 
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.
 
221
    """
 
222
    def __init__(self, dict):
 
223
        UserDict.UserDict.__init__(self, dict)
 
224
        self.already_warned = None
 
225
    def warn(self):
 
226
        if self.already_warned:
 
227
            return
 
228
        for k in self.keys():
 
229
            try:
 
230
                alt = misleading_keywords[k]
 
231
            except KeyError:
 
232
                pass
 
233
            else:
 
234
                SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning,
 
235
                                    "Did you mean to use `%s' instead of `%s'?" % (alt, k))
 
236
        self.already_warned = 1
44
237
 
45
238
def Builder(**kw):
46
239
    """A factory for builder objects."""
47
 
    
 
240
    composite = None
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']
53
 
 
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()
 
250
 
 
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)
 
259
            if not var:
 
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)
 
266
 
 
267
    if kw.has_key('src_builder'):
 
268
        ret = apply(MultiStepBuilder, (), kw)
58
269
    else:
59
 
        return apply(BuilderBase, (), kw)
60
 
 
61
 
 
62
 
 
63
 
def _init_nodes(builder, env, tlist, slist):
 
270
        ret = apply(BuilderBase, (), kw)
 
271
 
 
272
    if not composite is None:
 
273
        ret = CompositeBuilder(ret, composite)
 
274
 
 
275
    return ret
 
276
 
 
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.
66
280
    """
67
 
    src_scanner = None
68
 
    if slist:
69
 
        src_key = slist[0].scanner_key()        # the file suffix
70
 
        src_scanner = env.get_scanner(src_key)
71
 
        if src_scanner:
72
 
            src_scanner = src_scanner.instance(env)
73
 
 
74
 
    for t in tlist:
75
 
        t.cwd = SCons.Node.FS.default_fs.getcwd()       # XXX
 
281
 
 
282
    # First, figure out if there are any errors in the way the targets
 
283
    # were specified.
 
284
    for t in tlist:
 
285
        if t.side_effect:
 
286
            raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
 
287
        if t.has_builder():
 
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)
 
291
 
 
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)))
 
295
 
 
296
                else:
 
297
                    raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t)
 
298
 
 
299
            elif t.overrides != overrides:
 
300
                raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
 
301
 
 
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)
 
304
 
 
305
            if builder.multi:
 
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)
 
309
                    else:
 
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)
 
313
 
 
314
    if builder.single_source:
 
315
        if len(slist) > 1:
 
316
            raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
 
317
 
 
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
 
320
    # sources.
 
321
    executor = None
 
322
    if builder.multi:
 
323
        try:
 
324
            executor = tlist[0].get_executor(create = 0)
 
325
        except AttributeError:
 
326
            pass
 
327
        else:
 
328
            executor.add_sources(slist)
 
329
    if executor is None:
 
330
        executor = SCons.Executor.Executor(builder.action,
 
331
                                           env or builder.env,
 
332
                                           [builder.overrides, overrides],
 
333
                                           tlist,
 
334
                                           slist)
 
335
 
 
336
    # Now set up the relevant information in the target Nodes themselves.
 
337
    for t in tlist:
 
338
        t.overrides = overrides
 
339
        t.cwd = SCons.Node.FS.default_fs.getcwd()
76
340
        t.builder_set(builder)
77
341
        t.env_set(env)
78
342
        t.add_source(slist)
79
 
        if builder.scanner:
80
 
            t.scanner_set(builder.scanner.instance(env))
81
 
        if src_scanner:
82
 
            t.src_scanner_set(src_key, src_scanner)
83
 
 
84
 
 
 
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
 
348
 
 
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.
 
357
    for s in slist:
 
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())
 
360
 
 
361
class EmitterProxy:
 
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
 
367
    emitter."""
 
368
    def __init__(self, var):
 
369
        self.var = SCons.Util.to_String(var)
 
370
 
 
371
    def __call__(self, target, source, env):
 
372
        emitter = self.var
 
373
 
 
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):
 
382
            for e in emitter:
 
383
                target, source = e(target, source, env)
 
384
 
 
385
        return (target, source)
 
386
 
 
387
 
 
388
    def __cmp__(self, other):
 
389
        return cmp(self.var, other.var)
85
390
 
86
391
class BuilderBase:
87
392
    """Base class for Builders, objects that create output
88
393
    nodes (files) from input nodes (files).
89
394
    """
90
395
 
91
 
    def __init__(self,  name = None,
92
 
                        action = None,
93
 
                        prefix = '',
94
 
                        suffix = '',
95
 
                        src_suffix = '',
96
 
                        node_factory = SCons.Node.FS.default_fs.File,
97
 
                        target_factory = None,
98
 
                        source_factory = None,
99
 
                        scanner = None):
100
 
        if name is None:
101
 
            raise UserError, "You must specify a name for the builder."
102
 
        self.name = name
103
 
        self.action = SCons.Action.Action(action)
104
 
 
105
 
        self.prefix = prefix
106
 
        self.suffix = suffix
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,
 
397
                        prefix = '',
 
398
                        suffix = '',
 
399
                        src_suffix = '',
 
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,
 
404
                        emitter = None,
 
405
                        multi = 0,
 
406
                        env = None,
 
407
                        single_source = 0,
 
408
                        **overrides):
 
409
        if __debug__: logInstanceCreation(self, 'BuilderBase')
 
410
        self.action = SCons.Action.Action(action)
 
411
        self.multi = multi
 
412
        if SCons.Util.is_Dict(prefix):
 
413
            prefix = CallableSelector(prefix)
 
414
        self.prefix = prefix
 
415
        if SCons.Util.is_Dict(suffix):
 
416
            suffix = CallableSelector(suffix)
 
417
        self.suffix = suffix
 
418
        self.env = env
 
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
 
427
 
 
428
        self.set_src_suffix(src_suffix)
 
429
 
 
430
        self.target_factory = target_factory
 
431
        self.source_factory = source_factory
 
432
        self.target_scanner = target_scanner
 
433
        self.source_scanner = source_scanner
 
434
 
 
435
        self.emitter = emitter
 
436
 
 
437
    def __nonzero__(self):
 
438
        raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
 
439
 
 
440
    def get_name(self, env):
 
441
        """Attempts to get the name of the Builder.
 
442
 
 
443
        Look at the BUILDERS variable of env, expecting it to be a
 
444
        dictionary containing this Builder, and return the key of the
 
445
        dictionary."""
 
446
 
 
447
        try:
 
448
            index = env['BUILDERS'].values().index(self)
 
449
            return env['BUILDERS'].keys()[index]
 
450
        except (AttributeError, KeyError, ValueError):
 
451
            return str(self.__class__)
115
452
 
116
453
    def __cmp__(self, other):
117
 
        return cmp(self.__dict__, other.__dict__)
118
 
 
119
 
    def _create_nodes(self, env, target = None, source = None):
 
454
        return cmp(self.__dict__, other.__dict__)
 
455
 
 
456
    def splitext(self, path):
 
457
        return SCons.Util.splitext(path)
 
458
 
 
459
    def _create_nodes(self, env, overwarn, target = None, source = None):
120
460
        """Create and return lists of target and source nodes.
121
461
        """
122
 
        def adjustixes(files, pre, suf):
123
 
            ret = []
124
 
            if SCons.Util.is_String(files):
125
 
                files = string.split(files)
 
462
        def _adjustixes(files, pre, suf):
 
463
            if not files:
 
464
                return []
 
465
            result = []
126
466
            if not SCons.Util.is_List(files):
127
 
                files = [files]
128
 
            for f in files:
 
467
                files = [files]
 
468
 
 
469
            for f in 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)
133
 
                    if suf:
134
 
                        if f[-len(suf):] != suf:
135
 
                            f = f + suf
136
 
                ret.append(f)
137
 
            return ret
138
 
 
139
 
        tlist = SCons.Node.arg2nodes(adjustixes(target,
140
 
                                                env.subst(self.prefix),
141
 
                                                env.subst(self.suffix)),
142
 
                                     self.target_factory)
143
 
 
144
 
        slist = SCons.Node.arg2nodes(adjustixes(source,
145
 
                                                None,
146
 
                                                env.subst(self.src_suffix)),
147
 
                                     self.source_factory)
 
471
                    f = SCons.Util.adjustixes(f, pre, suf)
 
472
                result.append(f)
 
473
            return result
 
474
 
 
475
        overwarn.warn()
 
476
 
 
477
        env = env.Override(overwarn.data)
 
478
 
 
479
        src_suf = self.get_src_suffix(env)
 
480
 
 
481
        source = _adjustixes(source, None, src_suf)
 
482
        slist = env.arg2nodes(source, self.source_factory)
 
483
 
 
484
        pre = self.get_prefix(env, slist)
 
485
        suf = self.get_suffix(env, slist)
 
486
 
 
487
        if target is None:
 
488
            try:
 
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) ]
 
493
        else:
 
494
            target = _adjustixes(target, pre, suf)
 
495
            tlist = env.arg2nodes(target, self.target_factory)
 
496
 
 
497
        if self.emitter:
 
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.
 
503
            new_targets = []
 
504
            for t in tlist:
 
505
                if not t.is_derived():
 
506
                    t.builder = self
 
507
                    new_targets.append(t)
 
508
 
 
509
            target, source = self.emitter(target=tlist, source=slist, env=env)
 
510
 
 
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.
 
518
                    t.builder = None
 
519
 
 
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)
 
524
 
148
525
        return tlist, slist
149
526
 
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={}):
 
528
        if source is _null:
 
529
            source = target
 
530
            target = None
 
531
 
 
532
        if(self.single_source and
 
533
           SCons.Util.is_List(source) and
 
534
           len(source) > 1 and
 
535
           target is None):
 
536
            result = []
 
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):
 
541
                    result.extend(t)
 
542
                else:
 
543
                    result.append(t)
 
544
            return result
 
545
        
 
546
        tlist, slist = self._create_nodes(env, overwarn, target, source)
152
547
 
153
548
        if len(tlist) == 1:
154
 
            _init_nodes(self, env, tlist, slist)
155
 
            tlist = tlist[0]
 
549
            builder = self
156
550
        else:
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)
158
553
 
159
554
        return tlist
160
555
 
161
 
 
162
 
    def execute(self, **kw):
163
 
        """Execute a builder's action to create an output object.
164
 
        """
165
 
        return apply(self.action.execute, (), kw)
166
 
 
167
 
    def get_raw_contents(self, **kw):
168
 
        """Fetch the "contents" of the builder's action.
169
 
        """
170
 
        return apply(self.action.get_raw_contents, (), kw)
171
 
 
172
 
    def get_contents(self, **kw):
173
 
        """Fetch the "contents" of the builder's action
174
 
        (for signature calculation).
175
 
        """
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))
 
558
 
 
559
    def adjust_suffix(self, suff):
 
560
        if suff and not suff[0] in [ '.', '_', '$' ]:
 
561
            return '.' + suff
 
562
        return suff
 
563
 
 
564
    def get_prefix(self, env, sources=[]):
 
565
        prefix = self.prefix
 
566
        if callable(prefix):
 
567
            prefix = prefix(env, sources)
 
568
        return env.subst(prefix)
 
569
 
 
570
    def get_suffix(self, env, sources=[]):
 
571
        suffix = self.suffix
 
572
        if callable(suffix):
 
573
            suffix = suffix(env, sources)
 
574
        else:
 
575
            suffix = self.adjust_suffix(suffix)
 
576
        return env.subst(suffix)
177
577
 
178
578
    def src_suffixes(self, env):
179
 
        if self.src_suffix != '':
180
 
            return [env.subst(self.src_suffix)]
181
 
        return []
 
579
        return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)),
 
580
                   self.src_suffix)
 
581
 
 
582
    def set_src_suffix(self, src_suffix):
 
583
        if not src_suffix:
 
584
            src_suffix = []
 
585
        elif not SCons.Util.is_List(src_suffix):
 
586
            src_suffix = [ src_suffix ]
 
587
        self.src_suffix = src_suffix
 
588
 
 
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)
 
592
        if not ret:
 
593
            return ''
 
594
        return ret[0]
182
595
 
183
596
    def targets(self, node):
184
597
        """Return the list of targets for this builder instance.
187
600
        """
188
601
        return [ node ]
189
602
 
190
 
class ListBuilder:
191
 
    """This is technically not a Builder object, but a wrapper
192
 
    around another Builder object.  This is designed to look
193
 
    like a Builder object, though, for purposes of building an
194
 
    array of targets from a single Action execution.
 
603
    def add_emitter(self, suffix, emitter):
 
604
        """Add a suffix-emitter mapping to this Builder.
 
605
 
 
606
        This assumes that emitter has been initialized with an
 
607
        appropriate dictionary type, and will throw a TypeError if
 
608
        not, so the caller is responsible for knowing that this is an
 
609
        appropriate method to call for the Builder in question.
 
610
        """
 
611
        self.emitter[suffix] = emitter
 
612
 
 
613
class ListBuilder(SCons.Util.Proxy):
 
614
    """A Proxy to support building an array of targets (for example,
 
615
    foo.o and foo.h from foo.y) from a single Action execution.
195
616
    """
196
617
 
197
618
    def __init__(self, builder, env, tlist):
 
619
        if __debug__: logInstanceCreation(self)
 
620
        SCons.Util.Proxy.__init__(self, builder)
198
621
        self.builder = builder
199
 
        self.scanner = builder.scanner
 
622
        self.target_scanner = builder.target_scanner
 
623
        self.source_scanner = builder.source_scanner
200
624
        self.env = env
201
625
        self.tlist = tlist
202
 
 
203
 
    def execute(self, **kw):
204
 
        if hasattr(self, 'status'):
205
 
            return self.status
206
 
        for t in self.tlist:
207
 
            # unlink all targets and make all directories
208
 
            # before building anything
209
 
            t.prepare()
210
 
        kw['target'] = self.tlist[0]
211
 
        self.status = apply(self.builder.execute, (), kw)
212
 
        for t in self.tlist:
213
 
            if not t is kw['target']:
214
 
                t.build()
215
 
        return self.status
216
 
 
217
 
    def get_raw_contents(self, **kw):
218
 
        return apply(self.builder.get_raw_contents, (), kw)
219
 
 
220
 
    def get_contents(self, **kw):
221
 
        return apply(self.builder.get_contents, (), kw)
222
 
 
223
 
    def src_suffixes(self, env):
224
 
        return self.builder.src_suffixes(env)
 
626
        self.multi = builder.multi
 
627
        self.single_source = builder.single_source
225
628
 
226
629
    def targets(self, node):
227
630
        """Return the list of targets for this builder instance.
228
631
        """
229
632
        return self.tlist
230
633
 
 
634
    def __cmp__(self, other):
 
635
        return cmp(self.__dict__, other.__dict__)
 
636
 
 
637
    def get_name(self, env):
 
638
        """Attempts to get the name of the Builder."""
 
639
 
 
640
        return "ListBuilder(%s)" % self.builder.get_name(env)
 
641
 
231
642
class MultiStepBuilder(BuilderBase):
232
643
    """This is a builder subclass that can build targets in
233
644
    multiple steps.  The src_builder parameter to the constructor
240
651
    src_suffix.
241
652
    """
242
653
    def __init__(self,  src_builder,
243
 
                        name = None,
244
 
                        action = None,
245
 
                        prefix = '',
246
 
                        suffix = '',
247
 
                        src_suffix = '',
248
 
                        node_factory = SCons.Node.FS.default_fs.File,
249
 
                        target_factory = None,
250
 
                        source_factory = None,
251
 
                        scanner=None):
252
 
        BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
253
 
                             node_factory, target_factory, source_factory,
254
 
                             scanner)
 
654
                        action = None,
 
655
                        prefix = '',
 
656
                        suffix = '',
 
657
                        src_suffix = '',
 
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,
 
662
                        emitter=None,
 
663
                        single_source=0):
 
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
256
 
 
257
 
    def __call__(self, env, target = None, source = None):
258
 
        slist = SCons.Node.arg2nodes(source, self.source_factory)
 
672
        self.sdict = {}
 
673
        self.cached_src_suffixes = {} # source suffixes keyed on id(env)
 
674
 
 
675
    def _execute(self, env, target = None, source = _null, overwarn={}):
 
676
        if source is _null:
 
677
            source = target
 
678
            target = None
 
679
 
 
680
        slist = env.arg2nodes(source, self.source_factory)
259
681
        final_sources = []
260
 
        src_suffix = env.subst(self.src_suffix)
261
 
        sdict = {}
262
 
        for suff in self.src_builder.src_suffixes(env):
263
 
            sdict[suff] = None
 
682
 
 
683
        try:
 
684
            sdict = self.sdict[id(env)]
 
685
        except KeyError:
 
686
            sdict = {}
 
687
            self.sdict[id(env)] = sdict
 
688
            for bld in self.src_builder:
 
689
                if SCons.Util.is_String(bld):
 
690
                    try:
 
691
                        bld = env['BUILDERS'][bld]
 
692
                    except KeyError:
 
693
                        continue
 
694
                for suf in bld.src_suffixes(env):
 
695
                    sdict[suf] = bld
 
696
 
 
697
        src_suffixes = self.src_suffixes(env)
 
698
 
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)
270
 
                else:
271
 
                    final_sources.extend(tgt)
 
700
            try:
 
701
                get_suffix = snode.get_suffix
 
702
            except AttributeError:
 
703
                ext = self.splitext(str(snode))
272
704
            else:
 
705
                ext = get_suffix()
 
706
            try:
 
707
                subsidiary_builder = sdict[ext]
 
708
            except KeyError:
273
709
                final_sources.append(snode)
274
 
        return BuilderBase.__call__(self, env, target=target,
275
 
                                    source=final_sources)
276
 
 
277
 
    def src_suffixes(self, env):
278
 
        return BuilderBase.src_suffixes(self, env) + \
279
 
               self.src_builder.src_suffixes(env)
280
 
 
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,
289
 
                        prefix='',
290
 
                        suffix='',
291
 
                        action = {},
292
 
                        src_builder = []):
293
 
        BuilderBase.__init__(self, name=name, prefix=prefix,
294
 
                             suffix=suffix)
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
299
 
        self.sdict = {}
300
 
        self.sbuild = {}
301
 
 
302
 
    def __call__(self, env, target = None, source = None):
303
 
        tlist, slist = BuilderBase._create_nodes(self, env,
304
 
                                                 target=target, source=source)
305
 
 
306
 
        r = repr(env)
307
 
        if not self.sdict.has_key(r):
308
 
            self.sdict[r] = {}
309
 
            self.sbuild[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,
315
 
                                             self.src_builder))
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
320
 
 
321
 
        sufflist = map(lambda x, s=self.sdict[r]:
322
 
                              s[os.path.splitext(x.path)[1]],
323
 
                       slist)
324
 
        last_suffix = ''
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." % \
328
 
                      (tlist[0].path,
329
 
                       str(map(lambda t: str(t.path), tlist[0].sources)))
330
 
            last_suffix = suff
331
 
 
332
 
        if last_suffix:
333
 
            kw = {
334
 
                'name' : self.name,
335
 
                'action' : self.action_dict[last_suffix],
336
 
                'src_suffix' : last_suffix,
337
 
            }
338
 
            if self.sbuild[r]:
339
 
                sb = filter(lambda x, e=env, s=last_suffix:
340
 
                                   e.subst(x.suffix) == s,
341
 
                            self.sbuild[r])
342
 
                if sb:
343
 
                    kw['src_builder'] = sb[0]
344
 
            # XXX We should be able to cache this
345
 
            bld = apply(Builder, (), kw)
346
 
            for tnode in tlist:
347
 
                bld.__call__(env, target = tnode, source = slist)
348
 
 
349
 
        if len(tlist) == 1:
350
 
            tlist = tlist[0]
351
 
        return tlist
352
 
 
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),
357
 
                              self.src_builder),
358
 
                          [])
359
 
        return suffixes
 
710
            else:
 
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.
 
715
                if len(tgt) > 1:
 
716
                    tgt = filter(lambda x, self=self, suf=src_suffixes:
 
717
                                 self.splitext(SCons.Util.to_String(x))[1] in suf,
 
718
                                 tgt)
 
719
                final_sources.extend(tgt)
 
720
 
 
721
        return BuilderBase._execute(self, env, target, final_sources, overwarn)
 
722
 
 
723
    def get_src_builders(self, env):
 
724
        """Return all the src_builders for this Builder.
 
725
 
 
726
        This is essentially a recursive descent of the src_builder "tree."
 
727
        """
 
728
        ret = []
 
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.
 
733
                try:
 
734
                    bld = env['BUILDERS'][bld]
 
735
                except KeyError:
 
736
                    continue
 
737
            ret.append(bld)
 
738
        return ret
 
739
 
 
740
    def src_suffixes(self, env):
 
741
        """Return a list of the src_suffix attributes for all
 
742
        src_builders of this Builder.
 
743
        """
 
744
        try:
 
745
            return self.cached_src_suffixes[id(env)]
 
746
        except KeyError:
 
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
 
751
            return suffixes
 
752
 
 
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.
 
757
    """
 
758
 
 
759
    def __init__(self, builder, cmdgen):
 
760
        if __debug__: logInstanceCreation(self)
 
761
        SCons.Util.Proxy.__init__(self, builder)
 
762
 
 
763
        # cmdgen should always be an instance of DictCmdGenerator.
 
764
        self.cmdgen = cmdgen
 
765
        self.builder = builder
 
766
 
 
767
    def add_action(self, suffix, action):
 
768
        self.cmdgen.add_action(suffix, action)
 
769
        self.set_src_suffix(self.cmdgen.src_suffixes())
 
770
 
 
771
    def __cmp__(self, other):
 
772
        return cmp(self.__dict__, other.__dict__)