Complete CPPPATH work.
[scons.git] / src / engine / SCons / Scanner / __init__.py
1 """SCons.Scanner
2
3 The Scanner package for the SCons software construction utility.
4
5 """
6
7 #
8 # Copyright (c) 2001 Steven Knight
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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
31
32 __version__ = "__VERSION__"
33
34
35 from SCons.Util import scons_str2nodes
36
37
38 class _Null:
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 class Base:
46     """
47     The base class for dependency scanners.  This implements
48     straightforward, single-pass scanning of a single file.
49     """
50
51     def __init__(self, function, argument=_null, skeys=[]):
52         """
53         Construct a new scanner object given a scanner function.
54
55         'function' - a scanner function taking two or three arguments and
56         returning a list of strings.
57
58         'argument' - an optional argument that will be passed to the
59         scanner function if it is given.
60
61         'skeys; - an optional list argument that can be used to determine
62         which scanner should be used for a given Node. In the case of File
63         nodes, for example, the 'skeys' would be file suffixes.
64
65         The scanner function's first argument will be the name of a file
66         that should be scanned for dependencies, the second argument will
67         be an Environment object, the third argument will be the value
68         passed into 'argument', and the returned list should contain the
69         Nodes for all the direct dependencies of the file.
70
71         Examples:
72
73         s = Scanner(my_scanner_function)
74         
75         s = Scanner(function = my_scanner_function)
76
77         s = Scanner(function = my_scanner_function, argument = 'foo')
78
79         """
80
81         # Note: this class could easily work with scanner functions that take
82         # something other than a filename as an argument (e.g. a database
83         # node) and a dependencies list that aren't file names. All that
84         # would need to be changed is the documentation.
85
86         self.function = function
87         self.argument = argument
88         self.name = "NONE"
89         self.skeys = skeys
90
91     def scan(self, filename, env):
92         """
93         This method scans a single object. 'filename' is the filename
94         that will be passed to the scanner function, and 'env' is the
95         environment that will be passed to the scanner function. A list of
96         direct dependency nodes for the specified filename will be returned.
97         """
98
99         if not self.argument is _null:
100             return self.function(filename, env, self.argument)
101         else:
102             return self.function(filename, env)
103
104     def __cmp__(self, other):
105         return cmp(self.__dict__, other.__dict__)
106
107     def __call__(self, sources=None):
108         slist = scons_str2nodes(source, self.node_factory)
109         for s in slist:
110             s.scanner_set(self)
111
112         if len(slist) == 1:
113             slist = slist[0]
114         return slist
115
116 class Recursive(Base):
117     """
118     The class for recursive dependency scanning.  This will
119     re-scan any new files returned by each call to the
120     underlying scanning function, and return the aggregate
121     list of all dependencies.
122     """
123
124     def scan(self, filename, env):
125         """
126         This method does the actual scanning. 'filename' is the filename
127         that will be passed to the scanner function, and 'env' is the
128         environment that will be passed to the scanner function. An
129         aggregate list of dependency nodes for the specified filename
130         and any of its scanned dependencies will be returned.
131         """
132
133         files = [filename]
134         seen = [filename]
135         deps = []
136         while files:
137             f = files.pop(0)
138             if not self.argument is _null:
139                 d = self.function(f, env, self.argument)
140             else:
141                 d = self.function(f, env)
142             d = filter(lambda x, seen=seen: str(x) not in seen, d)
143             deps.extend(d)
144             s = map(str, d)
145             seen.extend(s)
146             files.extend(s)
147         return deps