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 2014/09/27 12:51:43 garyo"
31
32 import re
33
34 import SCons.Node.FS
35 import SCons.Util
36
37
40
41
42
43 _null = _Null
44
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
65 """A class to bind a specific *PATH variable name to a function that
66 will return all of the *path directories."""
68 self.variable = variable
69 - def __call__(self, env, dir=None, target=None, source=None, argument=None):
79
80
81
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
95
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
162
163
164
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
225 try:
226 return cmp(self.__dict__, other.__dict__)
227 except AttributeError:
228
229 return cmp(self.__dict__, other)
230
233
236
238 """Add a skey to the list of skeys"""
239 self.skeys.append(skey)
240
242 if env and SCons.Util.is_String(self.skeys):
243 return env.subst_list(self.skeys)[0]
244 return self.skeys
245
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
258
261
262 recurse_nodes = _recurse_no_nodes
263
265 self.function[skey] = scanner
266 self.add_skey(skey)
267
268
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 """
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 = ()):
288
290 try:
291 return self.dict[node.scanner_key()]
292 except KeyError:
293 return None
294
296 self.dict[skey] = scanner
297 self.add_skey(skey)
298
299
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
310 kw['scan_check'] = current_check
311 Base.__init__(self, *args, **kw)
312
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
346
349
352
353 - def scan(self, node, path=()):
354
355
356 if node.includes is not None:
357 includes = node.includes
358 else:
359 includes = self.find_include_names (node)
360
361
362 node.includes = list(map(SCons.Util.silent_intern, includes))
363
364
365
366
367
368
369
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
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 i = SCons.Util.silent_intern(include[1])
404 return n, i
405
408
409
410
411
412
413
414