~plane1/maus/devel_624

« back to all changes in this revision

Viewing changes to third_party/scons-2.0.1/lib/scons-2.0.1/SCons/Scanner/__init__.py

  • Committer: tunnell
  • Date: 2010-09-30 13:56:05 UTC
  • Revision ID: tunnell@itchy-20100930135605-wxbkfgy75p0sndk3
add third party

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""SCons.Scanner
 
2
 
 
3
The Scanner package for the SCons software construction utility.
 
4
 
 
5
"""
 
6
 
 
7
#
 
8
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation
 
9
#
 
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:
 
17
#
 
18
# The above copyright notice and this permission notice shall be included
 
19
# in all copies or substantial portions of the Software.
 
20
#
 
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.
 
28
#
 
29
 
 
30
__revision__ = "src/engine/SCons/Scanner/__init__.py 5134 2010/08/16 23:02:40 bdeegan"
 
31
 
 
32
import re
 
33
 
 
34
import SCons.Node.FS
 
35
import SCons.Util
 
36
 
 
37
 
 
38
class _Null(object):
 
39
    pass
 
40
 
 
41
# This is used instead of None as a default argument value so None can be
 
42
# used as an actual argument value.
 
43
_null = _Null
 
44
 
 
45
def Scanner(function, *args, **kw):
 
46
    """
 
47
    Public interface factory function for creating different types
 
48
    of Scanners based on the different types of "functions" that may
 
49
    be supplied.
 
50
 
 
51
    TODO:  Deprecate this some day.  We've moved the functionality
 
52
    inside the Base class and really don't need this factory function
 
53
    any more.  It was, however, used by some of our Tool modules, so
 
54
    the call probably ended up in various people's custom modules
 
55
    patterned on SCons code.
 
56
    """
 
57
    if SCons.Util.is_Dict(function):
 
58
        return Selector(function, *args, **kw)
 
59
    else:
 
60
        return Base(function, *args, **kw)
 
61
 
 
62
 
 
63
 
 
64
class FindPathDirs(object):
 
65
    """A class to bind a specific *PATH variable name to a function that
 
66
    will return all of the *path directories."""
 
67
    def __init__(self, variable):
 
68
        self.variable = variable
 
69
    def __call__(self, env, dir=None, target=None, source=None, argument=None):
 
70
        import SCons.PathList
 
71
        try:
 
72
            path = env[self.variable]
 
73
        except KeyError:
 
74
            return ()
 
75
 
 
76
        dir = dir or env.fs._cwd
 
77
        path = SCons.PathList.PathList(path).subst_path(env, target, source)
 
78
        return tuple(dir.Rfindalldirs(path))
 
79
 
 
80
 
 
81
 
 
82
class Base(object):
 
83
    """
 
84
    The base class for dependency scanners.  This implements
 
85
    straightforward, single-pass scanning of a single file.
 
86
    """
 
87
 
 
88
    def __init__(self,
 
89
                 function,
 
90
                 name = "NONE",
 
91
                 argument = _null,
 
92
                 skeys = _null,
 
93
                 path_function = None,
 
94
                 # Node.FS.Base so that, by default, it's okay for a
 
95
                 # scanner to return a Dir, File or Entry.
 
96
                 node_class = SCons.Node.FS.Base,
 
97
                 node_factory = None,
 
98
                 scan_check = None,
 
99
                 recursive = None):
 
100
        """
 
101
        Construct a new scanner object given a scanner function.
 
102
 
 
103
        'function' - a scanner function taking two or three
 
104
        arguments and returning a list of strings.
 
105
 
 
106
        'name' - a name for identifying this scanner object.
 
107
 
 
108
        'argument' - an optional argument that, if specified, will be
 
109
        passed to both the scanner function and the path_function.
 
110
 
 
111
        'skeys' - an optional list argument that can be used to determine
 
112
        which scanner should be used for a given Node. In the case of File
 
113
        nodes, for example, the 'skeys' would be file suffixes.
 
114
 
 
115
        'path_function' - a function that takes four or five arguments
 
116
        (a construction environment, Node for the directory containing
 
117
        the SConscript file that defined the primary target, list of
 
118
        target nodes, list of source nodes, and optional argument for
 
119
        this instance) and returns a tuple of the directories that can
 
120
        be searched for implicit dependency files.  May also return a
 
121
        callable() which is called with no args and returns the tuple
 
122
        (supporting Bindable class).
 
123
 
 
124
        'node_class' - the class of Nodes which this scan will return.
 
125
        If node_class is None, then this scanner will not enforce any
 
126
        Node conversion and will return the raw results from the
 
127
        underlying scanner function.
 
128
 
 
129
        'node_factory' - the factory function to be called to translate
 
130
        the raw results returned by the scanner function into the
 
131
        expected node_class objects.
 
132
 
 
133
        'scan_check' - a function to be called to first check whether
 
134
        this node really needs to be scanned.
 
135
 
 
136
        'recursive' - specifies that this scanner should be invoked
 
137
        recursively on all of the implicit dependencies it returns
 
138
        (the canonical example being #include lines in C source files).
 
139
        May be a callable, which will be called to filter the list
 
140
        of nodes found to select a subset for recursive scanning
 
141
        (the canonical example being only recursively scanning
 
142
        subdirectories within a directory).
 
143
 
 
144
        The scanner function's first argument will be a Node that should
 
145
        be scanned for dependencies, the second argument will be an
 
146
        Environment object, the third argument will be the tuple of paths
 
147
        returned by the path_function, and the fourth argument will be
 
148
        the value passed into 'argument', and the returned list should
 
149
        contain the Nodes for all the direct dependencies of the file.
 
150
 
 
151
        Examples:
 
152
 
 
153
        s = Scanner(my_scanner_function)
 
154
 
 
155
        s = Scanner(function = my_scanner_function)
 
156
 
 
157
        s = Scanner(function = my_scanner_function, argument = 'foo')
 
158
 
 
159
        """
 
160
 
 
161
        # Note: this class could easily work with scanner functions that take
 
162
        # something other than a filename as an argument (e.g. a database
 
163
        # node) and a dependencies list that aren't file names. All that
 
164
        # would need to be changed is the documentation.
 
165
 
 
166
        self.function = function
 
167
        self.path_function = path_function
 
168
        self.name = name
 
169
        self.argument = argument
 
170
 
 
171
        if skeys is _null:
 
172
            if SCons.Util.is_Dict(function):
 
173
                skeys = list(function.keys())
 
174
            else:
 
175
                skeys = []
 
176
        self.skeys = skeys
 
177
 
 
178
        self.node_class = node_class
 
179
        self.node_factory = node_factory
 
180
        self.scan_check = scan_check
 
181
        if callable(recursive):
 
182
            self.recurse_nodes = recursive
 
183
        elif recursive:
 
184
            self.recurse_nodes = self._recurse_all_nodes
 
185
        else:
 
186
            self.recurse_nodes = self._recurse_no_nodes
 
187
 
 
188
    def path(self, env, dir=None, target=None, source=None):
 
189
        if not self.path_function:
 
190
            return ()
 
191
        if not self.argument is _null:
 
192
            return self.path_function(env, dir, target, source, self.argument)
 
193
        else:
 
194
            return self.path_function(env, dir, target, source)
 
195
 
 
196
    def __call__(self, node, env, path = ()):
 
197
        """
 
198
        This method scans a single object. 'node' is the node
 
199
        that will be passed to the scanner function, and 'env' is the
 
200
        environment that will be passed to the scanner function. A list of
 
201
        direct dependency nodes for the specified node will be returned.
 
202
        """
 
203
        if self.scan_check and not self.scan_check(node, env):
 
204
            return []
 
205
 
 
206
        self = self.select(node)
 
207
 
 
208
        if not self.argument is _null:
 
209
            list = self.function(node, env, path, self.argument)
 
210
        else:
 
211
            list = self.function(node, env, path)
 
212
 
 
213
        kw = {}
 
214
        if hasattr(node, 'dir'):
 
215
            kw['directory'] = node.dir
 
216
        node_factory = env.get_factory(self.node_factory)
 
217
        nodes = []
 
218
        for l in list:
 
219
            if self.node_class and not isinstance(l, self.node_class):
 
220
                l = node_factory(l, **kw)
 
221
            nodes.append(l)
 
222
        return nodes
 
223
 
 
224
    def __cmp__(self, other):
 
225
        try:
 
226
            return cmp(self.__dict__, other.__dict__)
 
227
        except AttributeError:
 
228
            # other probably doesn't have a __dict__
 
229
            return cmp(self.__dict__, other)
 
230
 
 
231
    def __hash__(self):
 
232
        return id(self)
 
233
 
 
234
    def __str__(self):
 
235
        return self.name
 
236
 
 
237
    def add_skey(self, skey):
 
238
        """Add a skey to the list of skeys"""
 
239
        self.skeys.append(skey)
 
240
 
 
241
    def get_skeys(self, env=None):
 
242
        if env and SCons.Util.is_String(self.skeys):
 
243
            return env.subst_list(self.skeys)[0]
 
244
        return self.skeys
 
245
 
 
246
    def select(self, node):
 
247
        if SCons.Util.is_Dict(self.function):
 
248
            key = node.scanner_key()
 
249
            try:
 
250
                return self.function[key]
 
251
            except KeyError:
 
252
                return None
 
253
        else:
 
254
            return self
 
255
 
 
256
    def _recurse_all_nodes(self, nodes):
 
257
        return nodes
 
258
 
 
259
    def _recurse_no_nodes(self, nodes):
 
260
        return []
 
261
 
 
262
    recurse_nodes = _recurse_no_nodes
 
263
 
 
264
    def add_scanner(self, skey, scanner):
 
265
        self.function[skey] = scanner
 
266
        self.add_skey(skey)
 
267
 
 
268
 
 
269
class Selector(Base):
 
270
    """
 
271
    A class for selecting a more specific scanner based on the
 
272
    scanner_key() (suffix) for a specific Node.
 
273
 
 
274
    TODO:  This functionality has been moved into the inner workings of
 
275
    the Base class, and this class will be deprecated at some point.
 
276
    (It was never exposed directly as part of the public interface,
 
277
    although it is used by the Scanner() factory function that was
 
278
    used by various Tool modules and therefore was likely a template
 
279
    for custom modules that may be out there.)
 
280
    """
 
281
    def __init__(self, dict, *args, **kw):
 
282
        Base.__init__(self, None, *args, **kw)
 
283
        self.dict = dict
 
284
        self.skeys = list(dict.keys())
 
285
 
 
286
    def __call__(self, node, env, path = ()):
 
287
        return self.select(node)(node, env, path)
 
288
 
 
289
    def select(self, node):
 
290
        try:
 
291
            return self.dict[node.scanner_key()]
 
292
        except KeyError:
 
293
            return None
 
294
 
 
295
    def add_scanner(self, skey, scanner):
 
296
        self.dict[skey] = scanner
 
297
        self.add_skey(skey)
 
298
 
 
299
 
 
300
class Current(Base):
 
301
    """
 
302
    A class for scanning files that are source files (have no builder)
 
303
    or are derived files and are current (which implies that they exist,
 
304
    either locally or in a repository).
 
305
    """
 
306
 
 
307
    def __init__(self, *args, **kw):
 
308
        def current_check(node, env):
 
309
            return not node.has_builder() or node.is_up_to_date()
 
310
        kw['scan_check'] = current_check
 
311
        Base.__init__(self, *args, **kw)
 
312
 
 
313
class Classic(Current):
 
314
    """
 
315
    A Scanner subclass to contain the common logic for classic CPP-style
 
316
    include scanning, but which can be customized to use different
 
317
    regular expressions to find the includes.
 
318
 
 
319
    Note that in order for this to work "out of the box" (without
 
320
    overriding the find_include() and sort_key() methods), the regular
 
321
    expression passed to the constructor must return the name of the
 
322
    include file in group 0.
 
323
    """
 
324
 
 
325
    def __init__(self, name, suffixes, path_variable, regex, *args, **kw):
 
326
 
 
327
        self.cre = re.compile(regex, re.M)
 
328
 
 
329
        def _scan(node, env, path=(), self=self):
 
330
            node = node.rfile()
 
331
            if not node.exists():
 
332
                return []
 
333
            return self.scan(node, path)
 
334
 
 
335
        kw['function'] = _scan
 
336
        kw['path_function'] = FindPathDirs(path_variable)
 
337
        kw['recursive'] = 1
 
338
        kw['skeys'] = suffixes
 
339
        kw['name'] = name
 
340
 
 
341
        Current.__init__(self, *args, **kw)
 
342
 
 
343
    def find_include(self, include, source_dir, path):
 
344
        n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path))
 
345
        return n, include
 
346
 
 
347
    def sort_key(self, include):
 
348
        return SCons.Node.FS._my_normcase(include)
 
349
 
 
350
    def find_include_names(self, node):
 
351
        return self.cre.findall(node.get_text_contents())
 
352
 
 
353
    def scan(self, node, path=()):
 
354
 
 
355
        # cache the includes list in node so we only scan it once:
 
356
        if node.includes is not None:
 
357
            includes = node.includes
 
358
        else:
 
359
            includes = self.find_include_names (node)
 
360
            # Intern the names of the include files. Saves some memory
 
361
            # if the same header is included many times.
 
362
            node.includes = list(map(SCons.Util.silent_intern, includes))
 
363
 
 
364
        # This is a hand-coded DSU (decorate-sort-undecorate, or
 
365
        # Schwartzian transform) pattern.  The sort key is the raw name
 
366
        # of the file as specifed on the #include line (including the
 
367
        # " or <, since that may affect what file is found), which lets
 
368
        # us keep the sort order constant regardless of whether the file
 
369
        # is actually found in a Repository or locally.
 
370
        nodes = []
 
371
        source_dir = node.get_dir()
 
372
        if callable(path):
 
373
            path = path()
 
374
        for include in includes:
 
375
            n, i = self.find_include(include, source_dir, path)
 
376
 
 
377
            if n is None:
 
378
                SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
 
379
                                    "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
 
380
            else:
 
381
                nodes.append((self.sort_key(include), n))
 
382
 
 
383
        return [pair[1] for pair in sorted(nodes)]
 
384
 
 
385
class ClassicCPP(Classic):
 
386
    """
 
387
    A Classic Scanner subclass which takes into account the type of
 
388
    bracketing used to include the file, and uses classic CPP rules
 
389
    for searching for the files based on the bracketing.
 
390
 
 
391
    Note that in order for this to work, the regular expression passed
 
392
    to the constructor must return the leading bracket in group 0, and
 
393
    the contained filename in group 1.
 
394
    """
 
395
    def find_include(self, include, source_dir, path):
 
396
        if include[0] == '"':
 
397
            paths = (source_dir,) + tuple(path)
 
398
        else:
 
399
            paths = tuple(path) + (source_dir,)
 
400
 
 
401
        n = SCons.Node.FS.find_file(include[1], paths)
 
402
 
 
403
        i = SCons.Util.silent_intern(include[1])
 
404
        return n, i
 
405
 
 
406
    def sort_key(self, include):
 
407
        return SCons.Node.FS._my_normcase(' '.join(include))
 
408
 
 
409
# Local Variables:
 
410
# tab-width:4
 
411
# indent-tabs-mode:nil
 
412
# End:
 
413
# vim: set expandtab tabstop=4 shiftwidth=4: