3 The Scanner package for the SCons software construction utility.
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:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
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.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
42 # This is used instead of None as a default argument value so None can be
43 # used as an actual argument value.
47 """A class to bind a specific *PATH variable name and the fs object
48 to a function that will return all of the *path directories."""
49 def __init__(self, variable, fs):
50 self.variable = variable
52 def __call__(self, env, dir, argument=None):
54 path = env[self.variable]
58 return tuple(self.fs.Rsearchall(env.subst_path(path),
60 clazz = SCons.Node.FS.Dir,
65 The base class for dependency scanners. This implements
66 straightforward, single-pass scanning of a single file.
75 node_class = SCons.Node.FS.Entry,
76 node_factory = SCons.Node.FS.default_fs.File,
80 Construct a new scanner object given a scanner function.
82 'function' - a scanner function taking two or three
83 arguments and returning a list of strings.
85 'name' - a name for identifying this scanner object.
87 'argument' - an optional argument that, if specified, will be
88 passed to both the scanner function and the path_function.
90 'skeys' - an optional list argument that can be used to determine
91 which scanner should be used for a given Node. In the case of File
92 nodes, for example, the 'skeys' would be file suffixes.
94 'path_function' - a function that takes one to three arguments
95 (a construction environment, optional directory, and optional
96 argument for this instance) and returns a tuple of the
97 directories that can be searched for implicit dependency files.
99 'node_class' - the class of Nodes which this scan will return.
100 If node_class is None, then this scanner will not enforce any
101 Node conversion and will return the raw results from the
102 underlying scanner function.
104 'node_factory' - the factory function to be called to translate
105 the raw results returned by the scanner function into the
106 expected node_class objects.
108 'scan_check' - a function to be called to first check whether
109 this node really needs to be scanned.
111 'recursive' - specifies that this scanner should be invoked
112 recursively on the implicit dependencies it returns (the
113 canonical example being #include lines in C source files).
115 The scanner function's first argument will be the name of a file
116 that should be scanned for dependencies, the second argument will
117 be an Environment object, the third argument will be the value
118 passed into 'argument', and the returned list should contain the
119 Nodes for all the direct dependencies of the file.
123 s = Scanner(my_scanner_function)
125 s = Scanner(function = my_scanner_function)
127 s = Scanner(function = my_scanner_function, argument = 'foo')
131 # Note: this class could easily work with scanner functions that take
132 # something other than a filename as an argument (e.g. a database
133 # node) and a dependencies list that aren't file names. All that
134 # would need to be changed is the documentation.
136 self.function = function
137 self.path_function = path_function
139 self.argument = argument
141 self.node_class = node_class
142 self.node_factory = node_factory
143 self.scan_check = scan_check
144 self.recursive = recursive
146 def path(self, env, dir = None):
147 if not self.path_function:
149 if not self.argument is _null:
150 return self.path_function(env, dir, self.argument)
152 return self.path_function(env, dir)
154 def __call__(self, node, env, path = ()):
156 This method scans a single object. 'node' is the node
157 that will be passed to the scanner function, and 'env' is the
158 environment that will be passed to the scanner function. A list of
159 direct dependency nodes for the specified node will be returned.
161 if self.scan_check and not self.scan_check(node, env):
164 if not self.argument is _null:
165 list = self.function(node, env, path, self.argument)
167 list = self.function(node, env, path)
169 if hasattr(node, 'dir'):
170 kw['directory'] = node.dir
173 if self.node_class and not isinstance(l, self.node_class):
174 l = apply(self.node_factory, (l,), kw)
178 def __cmp__(self, other):
179 return cmp(self.__dict__, other.__dict__)
182 return hash(repr(self))
184 def add_skey(self, skey):
185 """Add a skey to the list of skeys"""
186 self.skeys.append(skey)
188 def get_skeys(self, env=None):
189 if SCons.Util.is_String(self.skeys):
190 return env.subst_list(self.skeys)[0]
196 A class for scanning files that are source files (have no builder)
197 or are derived files and are current (which implies that they exist,
198 either locally or in a repository).
201 def __init__(self, *args, **kw):
202 def current_check(node, env):
203 c = not node.has_builder() or node.current(env.get_calculator())
205 kw['scan_check'] = current_check
206 apply(Base.__init__, (self,) + args, kw)
208 class Classic(Current):
210 A Scanner subclass to contain the common logic for classic CPP-style
211 include scanning, but which can be customized to use different
212 regular expressions to find the includes.
214 Note that in order for this to work "out of the box" (without
215 overriding the find_include() method), the regular expression passed
216 to the constructor must return the name of the include file in group
220 def __init__(self, name, suffixes, path_variable, regex,
221 fs=SCons.Node.FS.default_fs, *args, **kw):
223 self.cre = re.compile(regex, re.M)
226 def _scan(node, env, path, self=self, fs=fs):
227 return self.scan(node, env, path)
229 kw['function'] = _scan
230 kw['path_function'] = FindPathDirs(path_variable, fs)
232 kw['skeys'] = suffixes
235 apply(Current.__init__, (self,) + args, kw)
237 def find_include(self, include, source_dir, path):
238 n = SCons.Node.FS.find_file(include, (source_dir,) + path, self.fs.File)
241 def scan(self, node, env, path=()):
244 if not node.exists():
247 # cache the includes list in node so we only scan it once:
248 if node.includes != None:
249 includes = node.includes
251 includes = self.cre.findall(node.get_contents())
252 node.includes = includes
255 source_dir = node.get_dir()
256 for include in includes:
257 n, i = self.find_include(include, source_dir, path)
262 SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
263 "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node))
265 # Schwartzian transform from the Python FAQ Wizard
266 def st(List, Metric):
267 def pairing(element, M = Metric):
268 return (M(element), element)
271 paired = map(pairing, List)
273 return map(stripit, paired)
276 # We don't want the order of includes to be
277 # modified by case changes on case insensitive OSes, so
278 # normalize the case of the filename here:
279 # (see test/win32pathmadness.py for a test of this)
280 return SCons.Node.FS._my_normcase(str(node))
282 transformed = st(nodes, normalize)
283 # print "Classic: " + str(node) + " => " + str(map(lambda x: str(x),list(transformed)))
286 class ClassicCPP(Classic):
288 A Classic Scanner subclass which takes into account the type of
289 bracketing used to include the file, and uses classic CPP rules
290 for searching for the files based on the bracketing.
292 Note that in order for this to work, the regular expression passed
293 to the constructor must return the leading bracket in group 0, and
294 the contained filename in group 1.
296 def find_include(self, include, source_dir, path):
297 if include[0] == '"':
298 n = SCons.Node.FS.find_file(include[1],
299 (source_dir,) + path,
302 n = SCons.Node.FS.find_file(include[1],
303 path + (source_dir,),