1 """SCons.Scanner
2
3 The Scanner package for the SCons software construction utility.
4
5 """
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 __revision__ = "src/engine/SCons/Scanner/__init__.py 3842 2008/12/20 22:59:52 scons"
31
32 import re
33 import string
34
35 import SCons.Node.FS
36 import SCons.Util
37
38
41
42
43
44 _null = _Null
45
47 """
48 Public interface factory function for creating different types
49 of Scanners based on the different types of "functions" that may
50 be supplied.
51
52 TODO: Deprecate this some day. We've moved the functionality
53 inside the Base class and really don't need this factory function
54 any more. It was, however, used by some of our Tool modules, so
55 the call probably ended up in various people's custom modules
56 patterned on SCons code.
57 """
58 if SCons.Util.is_Dict(function):
59 return apply(Selector, (function,) + args, kw)
60 else:
61 return apply(Base, (function,) + args, kw)
62
63
64
66 """A class to bind a specific *PATH variable name to a function that
67 will return all of the *path directories."""
69 self.variable = variable
70 - def __call__(self, env, dir=None, target=None, source=None, argument=None):
80
81
82
84 """
85 The base class for dependency scanners. This implements
86 straightforward, single-pass scanning of a single file.
87 """
88
89 - def __init__(self,
90 function,
91 name = "NONE",
92 argument = _null,
93 skeys = _null,
94 path_function = None,
95 node_class = SCons.Node.FS.Entry,
96 node_factory = None,
97 scan_check = None,
98 recursive = None):
99 """
100 Construct a new scanner object given a scanner function.
101
102 'function' - a scanner function taking two or three
103 arguments and returning a list of strings.
104
105 'name' - a name for identifying this scanner object.
106
107 'argument' - an optional argument that, if specified, will be
108 passed to both the scanner function and the path_function.
109
110 'skeys' - an optional list argument that can be used to determine
111 which scanner should be used for a given Node. In the case of File
112 nodes, for example, the 'skeys' would be file suffixes.
113
114 'path_function' - a function that takes four or five arguments
115 (a construction environment, Node for the directory containing
116 the SConscript file that defined the primary target, list of
117 target nodes, list of source nodes, and optional argument for
118 this instance) and returns a tuple of the directories that can
119 be searched for implicit dependency files. May also return a
120 callable() which is called with no args and returns the tuple
121 (supporting Bindable class).
122
123 'node_class' - the class of Nodes which this scan will return.
124 If node_class is None, then this scanner will not enforce any
125 Node conversion and will return the raw results from the
126 underlying scanner function.
127
128 'node_factory' - the factory function to be called to translate
129 the raw results returned by the scanner function into the
130 expected node_class objects.
131
132 'scan_check' - a function to be called to first check whether
133 this node really needs to be scanned.
134
135 'recursive' - specifies that this scanner should be invoked
136 recursively on all of the implicit dependencies it returns
137 (the canonical example being #include lines in C source files).
138 May be a callable, which will be called to filter the list
139 of nodes found to select a subset for recursive scanning
140 (the canonical example being only recursively scanning
141 subdirectories within a directory).
142
143 The scanner function's first argument will be a Node that should
144 be scanned for dependencies, the second argument will be an
145 Environment object, the third argument will be the tuple of paths
146 returned by the path_function, and the fourth argument will be
147 the value passed into 'argument', and the returned list should
148 contain the Nodes for all the direct dependencies of the file.
149
150 Examples:
151
152 s = Scanner(my_scanner_function)
153
154 s = Scanner(function = my_scanner_function)
155
156 s = Scanner(function = my_scanner_function, argument = 'foo')
157
158 """
159
160
161
162
163
164
165 self.function = function
166 self.path_function = path_function
167 self.name = name
168 self.argument = argument
169
170 if skeys is _null:
171 if SCons.Util.is_Dict(function):
172 skeys = function.keys()
173 else:
174 skeys = []
175 self.skeys = skeys
176
177 self.node_class = node_class
178 self.node_factory = node_factory
179 self.scan_check = scan_check
180 if callable(recursive):
181 self.recurse_nodes = recursive
182 elif recursive:
183 self.recurse_nodes = self._recurse_all_nodes
184 else:
185 self.recurse_nodes = self._recurse_no_nodes
186
187 - def path(self, env, dir=None, target=None, source=None):
188 if not self.path_function:
189 return ()
190 if not self.argument is _null:
191 return self.path_function(env, dir, target, source, self.argument)
192 else:
193 return self.path_function(env, dir, target, source)
194
195 - def __call__(self, node, env, path = ()):
196 """
197 This method scans a single object. 'node' is the node
198 that will be passed to the scanner function, and 'env' is the
199 environment that will be passed to the scanner function. A list of
200 direct dependency nodes for the specified node will be returned.
201 """
202 if self.scan_check and not self.scan_check(node, env):
203 return []
204
205 self = self.select(node)
206
207 if not self.argument is _null:
208 list = self.function(node, env, path, self.argument)
209 else:
210 list = self.function(node, env, path)
211
212 kw = {}
213 if hasattr(node, 'dir'):
214 kw['directory'] = node.dir
215 node_factory = env.get_factory(self.node_factory)
216 nodes = []
217 for l in list:
218 if self.node_class and not isinstance(l, self.node_class):
219 l = apply(node_factory, (l,), kw)
220 nodes.append(l)
221 return nodes
222
224 try:
225 return cmp(self.__dict__, other.__dict__)
226 except AttributeError:
227
228 return cmp(self.__dict__, other)
229
232
235
237 """Add a skey to the list of skeys"""
238 self.skeys.append(skey)
239
244
246 if SCons.Util.is_Dict(self.function):
247 key = node.scanner_key()
248 try:
249 return self.function[key]
250 except KeyError:
251 return None
252 else:
253 return self
254
257
260
261 recurse_nodes = _recurse_no_nodes
262
264 self.function[skey] = scanner
265 self.add_skey(skey)
266
267
269 """
270 A class for selecting a more specific scanner based on the
271 scanner_key() (suffix) for a specific Node.
272
273 TODO: This functionality has been moved into the inner workings of
274 the Base class, and this class will be deprecated at some point.
275 (It was never exposed directly as part of the public interface,
276 although it is used by the Scanner() factory function that was
277 used by various Tool modules and therefore was likely a template
278 for custom modules that may be out there.)
279 """
284
285 - def __call__(self, node, env, path = ()):
287
289 try:
290 return self.dict[node.scanner_key()]
291 except KeyError:
292 return None
293
297
298
300 """
301 A class for scanning files that are source files (have no builder)
302 or are derived files and are current (which implies that they exist,
303 either locally or in a repository).
304 """
305
309 kw['scan_check'] = current_check
310 apply(Base.__init__, (self,) + args, kw)
311
313 """
314 A Scanner subclass to contain the common logic for classic CPP-style
315 include scanning, but which can be customized to use different
316 regular expressions to find the includes.
317
318 Note that in order for this to work "out of the box" (without
319 overriding the find_include() and sort_key() methods), the regular
320 expression passed to the constructor must return the name of the
321 include file in group 0.
322 """
323
324 - def __init__(self, name, suffixes, path_variable, regex, *args, **kw):
325
326 self.cre = re.compile(regex, re.M)
327
328 def _scan(node, env, path=(), self=self):
329 node = node.rfile()
330 if not node.exists():
331 return []
332 return self.scan(node, path)
333
334 kw['function'] = _scan
335 kw['path_function'] = FindPathDirs(path_variable)
336 kw['recursive'] = 1
337 kw['skeys'] = suffixes
338 kw['name'] = name
339
340 apply(Current.__init__, (self,) + args, kw)
341
345
348
351
352 - def scan(self, node, path=()):
353
354
355 if node.includes != None:
356 includes = node.includes
357 else:
358 includes = self.find_include_names (node)
359 node.includes = includes
360
361
362
363
364
365
366
367 nodes = []
368 source_dir = node.get_dir()
369 if callable(path):
370 path = path()
371 for include in includes:
372 n, i = self.find_include(include, source_dir, path)
373
374 if n is None:
375 SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
376 "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
377 else:
378 sortkey = self.sort_key(include)
379 nodes.append((sortkey, n))
380
381 nodes.sort()
382 nodes = map(lambda pair: pair[1], nodes)
383 return nodes
384
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 """
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 return n, include[1]
404
407