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 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan"
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 """
66 A class to bind a specific E{*}PATH variable name to a function that
67 will return all of the E{*}path directories.
68 """
70 self.variable = variable
71 - def __call__(self, env, dir=None, target=None, source=None, argument=None):
81
82
83
85 """
86 The base class for dependency scanners. This implements
87 straightforward, single-pass scanning of a single file.
88 """
89
90 - def __init__(self,
91 function,
92 name = "NONE",
93 argument = _null,
94 skeys = _null,
95 path_function = None,
96
97
98 node_class = SCons.Node.FS.Base,
99 node_factory = None,
100 scan_check = None,
101 recursive = None):
102 """
103 Construct a new scanner object given a scanner function.
104
105 'function' - a scanner function taking two or three
106 arguments and returning a list of strings.
107
108 'name' - a name for identifying this scanner object.
109
110 'argument' - an optional argument that, if specified, will be
111 passed to both the scanner function and the path_function.
112
113 'skeys' - an optional list argument that can be used to determine
114 which scanner should be used for a given Node. In the case of File
115 nodes, for example, the 'skeys' would be file suffixes.
116
117 'path_function' - a function that takes four or five arguments
118 (a construction environment, Node for the directory containing
119 the SConscript file that defined the primary target, list of
120 target nodes, list of source nodes, and optional argument for
121 this instance) and returns a tuple of the directories that can
122 be searched for implicit dependency files. May also return a
123 callable() which is called with no args and returns the tuple
124 (supporting Bindable class).
125
126 'node_class' - the class of Nodes which this scan will return.
127 If node_class is None, then this scanner will not enforce any
128 Node conversion and will return the raw results from the
129 underlying scanner function.
130
131 'node_factory' - the factory function to be called to translate
132 the raw results returned by the scanner function into the
133 expected node_class objects.
134
135 'scan_check' - a function to be called to first check whether
136 this node really needs to be scanned.
137
138 'recursive' - specifies that this scanner should be invoked
139 recursively on all of the implicit dependencies it returns
140 (the canonical example being #include lines in C source files).
141 May be a callable, which will be called to filter the list
142 of nodes found to select a subset for recursive scanning
143 (the canonical example being only recursively scanning
144 subdirectories within a directory).
145
146 The scanner function's first argument will be a Node that should
147 be scanned for dependencies, the second argument will be an
148 Environment object, the third argument will be the tuple of paths
149 returned by the path_function, and the fourth argument will be
150 the value passed into 'argument', and the returned list should
151 contain the Nodes for all the direct dependencies of the file.
152
153 Examples:
154
155 s = Scanner(my_scanner_function)
156
157 s = Scanner(function = my_scanner_function)
158
159 s = Scanner(function = my_scanner_function, argument = 'foo')
160
161 """
162
163
164
165
166
167
168 self.function = function
169 self.path_function = path_function
170 self.name = name
171 self.argument = argument
172
173 if skeys is _null:
174 if SCons.Util.is_Dict(function):
175 skeys = list(function.keys())
176 else:
177 skeys = []
178 self.skeys = skeys
179
180 self.node_class = node_class
181 self.node_factory = node_factory
182 self.scan_check = scan_check
183 if callable(recursive):
184 self.recurse_nodes = recursive
185 elif recursive:
186 self.recurse_nodes = self._recurse_all_nodes
187 else:
188 self.recurse_nodes = self._recurse_no_nodes
189
190 - def path(self, env, dir=None, target=None, source=None):
191 if not self.path_function:
192 return ()
193 if self.argument is not _null:
194 return self.path_function(env, dir, target, source, self.argument)
195 else:
196 return self.path_function(env, dir, target, source)
197
198 - def __call__(self, node, env, path=()):
199 """
200 This method scans a single object. 'node' is the node
201 that will be passed to the scanner function, and 'env' is the
202 environment that will be passed to the scanner function. A list of
203 direct dependency nodes for the specified node will be returned.
204 """
205 if self.scan_check and not self.scan_check(node, env):
206 return []
207
208 self = self.select(node)
209
210 if not self.argument is _null:
211 node_list = self.function(node, env, path, self.argument)
212 else:
213 node_list = self.function(node, env, path)
214
215 kw = {}
216 if hasattr(node, 'dir'):
217 kw['directory'] = node.dir
218 node_factory = env.get_factory(self.node_factory)
219 nodes = []
220 for l in node_list:
221 if self.node_class and not isinstance(l, self.node_class):
222 l = node_factory(l, **kw)
223 nodes.append(l)
224 return nodes
225
227 try:
228 return self.__dict__ == other.__dict__
229 except AttributeError:
230
231 return self.__dict__ == other
232
235
238
240 """Add a skey to the list of skeys"""
241 self.skeys.append(skey)
242
247
249 if SCons.Util.is_Dict(self.function):
250 key = node.scanner_key()
251 try:
252 return self.function[key]
253 except KeyError:
254 return None
255 else:
256 return self
257
260
263
264
265
267 self.function[skey] = scanner
268 self.add_skey(skey)
269
270
272 """
273 A class for selecting a more specific scanner based on the
274 scanner_key() (suffix) for a specific Node.
275
276 TODO: This functionality has been moved into the inner workings of
277 the Base class, and this class will be deprecated at some point.
278 (It was never exposed directly as part of the public interface,
279 although it is used by the Scanner() factory function that was
280 used by various Tool modules and therefore was likely a template
281 for custom modules that may be out there.)
282 """
284 Base.__init__(self, None, *args, **kw)
285 self.dict = dict
286 self.skeys = list(dict.keys())
287
288 - def __call__(self, node, env, path=()):
290
292 try:
293 return self.dict[node.scanner_key()]
294 except KeyError:
295 return None
296
298 self.dict[skey] = scanner
299 self.add_skey(skey)
300
301
303 """
304 A class for scanning files that are source files (have no builder)
305 or are derived files and are current (which implies that they exist,
306 either locally or in a repository).
307 """
308
312 kw['scan_check'] = current_check
313 Base.__init__(self, *args, **kw)
314
316 """
317 A Scanner subclass to contain the common logic for classic CPP-style
318 include scanning, but which can be customized to use different
319 regular expressions to find the includes.
320
321 Note that in order for this to work "out of the box" (without
322 overriding the find_include() and sort_key() methods), the regular
323 expression passed to the constructor must return the name of the
324 include file in group 0.
325 """
326
327 - def __init__(self, name, suffixes, path_variable, regex, *args, **kw):
328
329 self.cre = re.compile(regex, re.M)
330
331 def _scan(node, _, path=(), self=self):
332 node = node.rfile()
333 if not node.exists():
334 return []
335 return self.scan(node, path)
336
337 kw['function'] = _scan
338 kw['path_function'] = FindPathDirs(path_variable)
339
340
341
342
343
344 kw['recursive'] = kw.get('recursive', 1)
345 kw['skeys'] = suffixes
346 kw['name'] = name
347
348 Current.__init__(self, *args, **kw)
349
353
356
359
360 - def scan(self, node, path=()):
391
393 """
394 A Classic Scanner subclass which takes into account the type of
395 bracketing used to include the file, and uses classic CPP rules
396 for searching for the files based on the bracketing.
397
398 Note that in order for this to work, the regular expression passed
399 to the constructor must return the leading bracket in group 0, and
400 the contained filename in group 1.
401 """
403 include = list(map(SCons.Util.to_str, include))
404 if include[0] == '"':
405 paths = (source_dir,) + tuple(path)
406 else:
407 paths = tuple(path) + (source_dir,)
408
409 n = SCons.Node.FS.find_file(include[1], paths)
410
411 i = SCons.Util.silent_intern(include[1])
412 return n, i
413
416
417
418
419
420
421
422