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 5110 2010/07/25 16:14:38 bdeegan"
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
96
97 node_class = SCons.Node.FS.Base,
98 node_factory = None,
99 scan_check = None,
100 recursive = None):
101 """
102 Construct a new scanner object given a scanner function.
103
104 'function' - a scanner function taking two or three
105 arguments and returning a list of strings.
106
107 'name' - a name for identifying this scanner object.
108
109 'argument' - an optional argument that, if specified, will be
110 passed to both the scanner function and the path_function.
111
112 'skeys' - an optional list argument that can be used to determine
113 which scanner should be used for a given Node. In the case of File
114 nodes, for example, the 'skeys' would be file suffixes.
115
116 'path_function' - a function that takes four or five arguments
117 (a construction environment, Node for the directory containing
118 the SConscript file that defined the primary target, list of
119 target nodes, list of source nodes, and optional argument for
120 this instance) and returns a tuple of the directories that can
121 be searched for implicit dependency files. May also return a
122 callable() which is called with no args and returns the tuple
123 (supporting Bindable class).
124
125 'node_class' - the class of Nodes which this scan will return.
126 If node_class is None, then this scanner will not enforce any
127 Node conversion and will return the raw results from the
128 underlying scanner function.
129
130 'node_factory' - the factory function to be called to translate
131 the raw results returned by the scanner function into the
132 expected node_class objects.
133
134 'scan_check' - a function to be called to first check whether
135 this node really needs to be scanned.
136
137 'recursive' - specifies that this scanner should be invoked
138 recursively on all of the implicit dependencies it returns
139 (the canonical example being #include lines in C source files).
140 May be a callable, which will be called to filter the list
141 of nodes found to select a subset for recursive scanning
142 (the canonical example being only recursively scanning
143 subdirectories within a directory).
144
145 The scanner function's first argument will be a Node that should
146 be scanned for dependencies, the second argument will be an
147 Environment object, the third argument will be the tuple of paths
148 returned by the path_function, and the fourth argument will be
149 the value passed into 'argument', and the returned list should
150 contain the Nodes for all the direct dependencies of the file.
151
152 Examples:
153
154 s = Scanner(my_scanner_function)
155
156 s = Scanner(function = my_scanner_function)
157
158 s = Scanner(function = my_scanner_function, argument = 'foo')
159
160 """
161
162
163
164
165
166
167 self.function = function
168 self.path_function = path_function
169 self.name = name
170 self.argument = argument
171
172 if skeys is _null:
173 if SCons.Util.is_Dict(function):
174 skeys = function.keys()
175 else:
176 skeys = []
177 self.skeys = skeys
178
179 self.node_class = node_class
180 self.node_factory = node_factory
181 self.scan_check = scan_check
182 if callable(recursive):
183 self.recurse_nodes = recursive
184 elif recursive:
185 self.recurse_nodes = self._recurse_all_nodes
186 else:
187 self.recurse_nodes = self._recurse_no_nodes
188
189 - def path(self, env, dir=None, target=None, source=None):
190 if not self.path_function:
191 return ()
192 if not self.argument is _null:
193 return self.path_function(env, dir, target, source, self.argument)
194 else:
195 return self.path_function(env, dir, target, source)
196
197 - def __call__(self, node, env, path = ()):
198 """
199 This method scans a single object. 'node' is the node
200 that will be passed to the scanner function, and 'env' is the
201 environment that will be passed to the scanner function. A list of
202 direct dependency nodes for the specified node will be returned.
203 """
204 if self.scan_check and not self.scan_check(node, env):
205 return []
206
207 self = self.select(node)
208
209 if not self.argument is _null:
210 list = self.function(node, env, path, self.argument)
211 else:
212 list = self.function(node, env, path)
213
214 kw = {}
215 if hasattr(node, 'dir'):
216 kw['directory'] = node.dir
217 node_factory = env.get_factory(self.node_factory)
218 nodes = []
219 for l in list:
220 if self.node_class and not isinstance(l, self.node_class):
221 l = apply(node_factory, (l,), kw)
222 nodes.append(l)
223 return nodes
224
226 try:
227 return cmp(self.__dict__, other.__dict__)
228 except AttributeError:
229
230 return cmp(self.__dict__, other)
231
234
237
239 """Add a skey to the list of skeys"""
240 self.skeys.append(skey)
241
246
248 if SCons.Util.is_Dict(self.function):
249 key = node.scanner_key()
250 try:
251 return self.function[key]
252 except KeyError:
253 return None
254 else:
255 return self
256
259
262
263 recurse_nodes = _recurse_no_nodes
264
266 self.function[skey] = scanner
267 self.add_skey(skey)
268
269
271 """
272 A class for selecting a more specific scanner based on the
273 scanner_key() (suffix) for a specific Node.
274
275 TODO: This functionality has been moved into the inner workings of
276 the Base class, and this class will be deprecated at some point.
277 (It was never exposed directly as part of the public interface,
278 although it is used by the Scanner() factory function that was
279 used by various Tool modules and therefore was likely a template
280 for custom modules that may be out there.)
281 """
286
287 - def __call__(self, node, env, path = ()):
289
291 try:
292 return self.dict[node.scanner_key()]
293 except KeyError:
294 return None
295
299
300
302 """
303 A class for scanning files that are source files (have no builder)
304 or are derived files and are current (which implies that they exist,
305 either locally or in a repository).
306 """
307
311 kw['scan_check'] = current_check
312 apply(Base.__init__, (self,) + args, kw)
313
315 """
316 A Scanner subclass to contain the common logic for classic CPP-style
317 include scanning, but which can be customized to use different
318 regular expressions to find the includes.
319
320 Note that in order for this to work "out of the box" (without
321 overriding the find_include() and sort_key() methods), the regular
322 expression passed to the constructor must return the name of the
323 include file in group 0.
324 """
325
326 - def __init__(self, name, suffixes, path_variable, regex, *args, **kw):
327
328 self.cre = re.compile(regex, re.M)
329
330 def _scan(node, env, path=(), self=self):
331 node = node.rfile()
332 if not node.exists():
333 return []
334 return self.scan(node, path)
335
336 kw['function'] = _scan
337 kw['path_function'] = FindPathDirs(path_variable)
338 kw['recursive'] = 1
339 kw['skeys'] = suffixes
340 kw['name'] = name
341
342 apply(Current.__init__, (self,) + args, kw)
343
347
350
353
354 - def scan(self, node, path=()):
355
356
357 if node.includes is not None:
358 includes = node.includes
359 else:
360 includes = self.find_include_names (node)
361
362
363 node.includes = map(SCons.Util.silent_intern, includes)
364
365
366
367
368
369
370
371 nodes = []
372 source_dir = node.get_dir()
373 if callable(path):
374 path = path()
375 for include in includes:
376 n, i = self.find_include(include, source_dir, path)
377
378 if n is None:
379 SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
380 "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
381 else:
382 sortkey = self.sort_key(include)
383 nodes.append((sortkey, n))
384
385 nodes.sort()
386 nodes = map(lambda pair: pair[1], nodes)
387 return nodes
388
390 """
391 A Classic Scanner subclass which takes into account the type of
392 bracketing used to include the file, and uses classic CPP rules
393 for searching for the files based on the bracketing.
394
395 Note that in order for this to work, the regular expression passed
396 to the constructor must return the leading bracket in group 0, and
397 the contained filename in group 1.
398 """
400 if include[0] == '"':
401 paths = (source_dir,) + tuple(path)
402 else:
403 paths = tuple(path) + (source_dir,)
404
405 n = SCons.Node.FS.find_file(include[1], paths)
406
407 i = SCons.Util.silent_intern(include[1])
408 return n, i
409
412
413
414
415
416
417
418