Index: scons-2.0.1/engine/SCons/Scanner/__init__.py |
=================================================================== |
--- scons-2.0.1/engine/SCons/Scanner/__init__.py (revision 0) |
+++ scons-2.0.1/engine/SCons/Scanner/__init__.py (revision 0) |
@@ -0,0 +1,413 @@ |
+"""SCons.Scanner |
+ |
+The Scanner package for the SCons software construction utility. |
+ |
+""" |
+ |
+# |
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining |
+# a copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, |
+# distribute, sublicense, and/or sell copies of the Software, and to |
+# permit persons to whom the Software is furnished to do so, subject to |
+# the following conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+# |
+ |
+__revision__ = "src/engine/SCons/Scanner/__init__.py 5134 2010/08/16 23:02:40 bdeegan" |
+ |
+import re |
+ |
+import SCons.Node.FS |
+import SCons.Util |
+ |
+ |
+class _Null(object): |
+ pass |
+ |
+# This is used instead of None as a default argument value so None can be |
+# used as an actual argument value. |
+_null = _Null |
+ |
+def Scanner(function, *args, **kw): |
+ """ |
+ Public interface factory function for creating different types |
+ of Scanners based on the different types of "functions" that may |
+ be supplied. |
+ |
+ TODO: Deprecate this some day. We've moved the functionality |
+ inside the Base class and really don't need this factory function |
+ any more. It was, however, used by some of our Tool modules, so |
+ the call probably ended up in various people's custom modules |
+ patterned on SCons code. |
+ """ |
+ if SCons.Util.is_Dict(function): |
+ return Selector(function, *args, **kw) |
+ else: |
+ return Base(function, *args, **kw) |
+ |
+ |
+ |
+class FindPathDirs(object): |
+ """A class to bind a specific *PATH variable name to a function that |
+ will return all of the *path directories.""" |
+ def __init__(self, variable): |
+ self.variable = variable |
+ def __call__(self, env, dir=None, target=None, source=None, argument=None): |
+ import SCons.PathList |
+ try: |
+ path = env[self.variable] |
+ except KeyError: |
+ return () |
+ |
+ dir = dir or env.fs._cwd |
+ path = SCons.PathList.PathList(path).subst_path(env, target, source) |
+ return tuple(dir.Rfindalldirs(path)) |
+ |
+ |
+ |
+class Base(object): |
+ """ |
+ The base class for dependency scanners. This implements |
+ straightforward, single-pass scanning of a single file. |
+ """ |
+ |
+ def __init__(self, |
+ function, |
+ name = "NONE", |
+ argument = _null, |
+ skeys = _null, |
+ path_function = None, |
+ # Node.FS.Base so that, by default, it's okay for a |
+ # scanner to return a Dir, File or Entry. |
+ node_class = SCons.Node.FS.Base, |
+ node_factory = None, |
+ scan_check = None, |
+ recursive = None): |
+ """ |
+ Construct a new scanner object given a scanner function. |
+ |
+ 'function' - a scanner function taking two or three |
+ arguments and returning a list of strings. |
+ |
+ 'name' - a name for identifying this scanner object. |
+ |
+ 'argument' - an optional argument that, if specified, will be |
+ passed to both the scanner function and the path_function. |
+ |
+ 'skeys' - an optional list argument that can be used to determine |
+ which scanner should be used for a given Node. In the case of File |
+ nodes, for example, the 'skeys' would be file suffixes. |
+ |
+ 'path_function' - a function that takes four or five arguments |
+ (a construction environment, Node for the directory containing |
+ the SConscript file that defined the primary target, list of |
+ target nodes, list of source nodes, and optional argument for |
+ this instance) and returns a tuple of the directories that can |
+ be searched for implicit dependency files. May also return a |
+ callable() which is called with no args and returns the tuple |
+ (supporting Bindable class). |
+ |
+ 'node_class' - the class of Nodes which this scan will return. |
+ If node_class is None, then this scanner will not enforce any |
+ Node conversion and will return the raw results from the |
+ underlying scanner function. |
+ |
+ 'node_factory' - the factory function to be called to translate |
+ the raw results returned by the scanner function into the |
+ expected node_class objects. |
+ |
+ 'scan_check' - a function to be called to first check whether |
+ this node really needs to be scanned. |
+ |
+ 'recursive' - specifies that this scanner should be invoked |
+ recursively on all of the implicit dependencies it returns |
+ (the canonical example being #include lines in C source files). |
+ May be a callable, which will be called to filter the list |
+ of nodes found to select a subset for recursive scanning |
+ (the canonical example being only recursively scanning |
+ subdirectories within a directory). |
+ |
+ The scanner function's first argument will be a Node that should |
+ be scanned for dependencies, the second argument will be an |
+ Environment object, the third argument will be the tuple of paths |
+ returned by the path_function, and the fourth argument will be |
+ the value passed into 'argument', and the returned list should |
+ contain the Nodes for all the direct dependencies of the file. |
+ |
+ Examples: |
+ |
+ s = Scanner(my_scanner_function) |
+ |
+ s = Scanner(function = my_scanner_function) |
+ |
+ s = Scanner(function = my_scanner_function, argument = 'foo') |
+ |
+ """ |
+ |
+ # Note: this class could easily work with scanner functions that take |
+ # something other than a filename as an argument (e.g. a database |
+ # node) and a dependencies list that aren't file names. All that |
+ # would need to be changed is the documentation. |
+ |
+ self.function = function |
+ self.path_function = path_function |
+ self.name = name |
+ self.argument = argument |
+ |
+ if skeys is _null: |
+ if SCons.Util.is_Dict(function): |
+ skeys = list(function.keys()) |
+ else: |
+ skeys = [] |
+ self.skeys = skeys |
+ |
+ self.node_class = node_class |
+ self.node_factory = node_factory |
+ self.scan_check = scan_check |
+ if callable(recursive): |
+ self.recurse_nodes = recursive |
+ elif recursive: |
+ self.recurse_nodes = self._recurse_all_nodes |
+ else: |
+ self.recurse_nodes = self._recurse_no_nodes |
+ |
+ def path(self, env, dir=None, target=None, source=None): |
+ if not self.path_function: |
+ return () |
+ if not self.argument is _null: |
+ return self.path_function(env, dir, target, source, self.argument) |
+ else: |
+ return self.path_function(env, dir, target, source) |
+ |
+ def __call__(self, node, env, path = ()): |
+ """ |
+ This method scans a single object. 'node' is the node |
+ that will be passed to the scanner function, and 'env' is the |
+ environment that will be passed to the scanner function. A list of |
+ direct dependency nodes for the specified node will be returned. |
+ """ |
+ if self.scan_check and not self.scan_check(node, env): |
+ return [] |
+ |
+ self = self.select(node) |
+ |
+ if not self.argument is _null: |
+ list = self.function(node, env, path, self.argument) |
+ else: |
+ list = self.function(node, env, path) |
+ |
+ kw = {} |
+ if hasattr(node, 'dir'): |
+ kw['directory'] = node.dir |
+ node_factory = env.get_factory(self.node_factory) |
+ nodes = [] |
+ for l in list: |
+ if self.node_class and not isinstance(l, self.node_class): |
+ l = node_factory(l, **kw) |
+ nodes.append(l) |
+ return nodes |
+ |
+ def __cmp__(self, other): |
+ try: |
+ return cmp(self.__dict__, other.__dict__) |
+ except AttributeError: |
+ # other probably doesn't have a __dict__ |
+ return cmp(self.__dict__, other) |
+ |
+ def __hash__(self): |
+ return id(self) |
+ |
+ def __str__(self): |
+ return self.name |
+ |
+ def add_skey(self, skey): |
+ """Add a skey to the list of skeys""" |
+ self.skeys.append(skey) |
+ |
+ def get_skeys(self, env=None): |
+ if env and SCons.Util.is_String(self.skeys): |
+ return env.subst_list(self.skeys)[0] |
+ return self.skeys |
+ |
+ def select(self, node): |
+ if SCons.Util.is_Dict(self.function): |
+ key = node.scanner_key() |
+ try: |
+ return self.function[key] |
+ except KeyError: |
+ return None |
+ else: |
+ return self |
+ |
+ def _recurse_all_nodes(self, nodes): |
+ return nodes |
+ |
+ def _recurse_no_nodes(self, nodes): |
+ return [] |
+ |
+ recurse_nodes = _recurse_no_nodes |
+ |
+ def add_scanner(self, skey, scanner): |
+ self.function[skey] = scanner |
+ self.add_skey(skey) |
+ |
+ |
+class Selector(Base): |
+ """ |
+ A class for selecting a more specific scanner based on the |
+ scanner_key() (suffix) for a specific Node. |
+ |
+ TODO: This functionality has been moved into the inner workings of |
+ the Base class, and this class will be deprecated at some point. |
+ (It was never exposed directly as part of the public interface, |
+ although it is used by the Scanner() factory function that was |
+ used by various Tool modules and therefore was likely a template |
+ for custom modules that may be out there.) |
+ """ |
+ def __init__(self, dict, *args, **kw): |
+ Base.__init__(self, None, *args, **kw) |
+ self.dict = dict |
+ self.skeys = list(dict.keys()) |
+ |
+ def __call__(self, node, env, path = ()): |
+ return self.select(node)(node, env, path) |
+ |
+ def select(self, node): |
+ try: |
+ return self.dict[node.scanner_key()] |
+ except KeyError: |
+ return None |
+ |
+ def add_scanner(self, skey, scanner): |
+ self.dict[skey] = scanner |
+ self.add_skey(skey) |
+ |
+ |
+class Current(Base): |
+ """ |
+ A class for scanning files that are source files (have no builder) |
+ or are derived files and are current (which implies that they exist, |
+ either locally or in a repository). |
+ """ |
+ |
+ def __init__(self, *args, **kw): |
+ def current_check(node, env): |
+ return not node.has_builder() or node.is_up_to_date() |
+ kw['scan_check'] = current_check |
+ Base.__init__(self, *args, **kw) |
+ |
+class Classic(Current): |
+ """ |
+ A Scanner subclass to contain the common logic for classic CPP-style |
+ include scanning, but which can be customized to use different |
+ regular expressions to find the includes. |
+ |
+ Note that in order for this to work "out of the box" (without |
+ overriding the find_include() and sort_key() methods), the regular |
+ expression passed to the constructor must return the name of the |
+ include file in group 0. |
+ """ |
+ |
+ def __init__(self, name, suffixes, path_variable, regex, *args, **kw): |
+ |
+ self.cre = re.compile(regex, re.M) |
+ |
+ def _scan(node, env, path=(), self=self): |
+ node = node.rfile() |
+ if not node.exists(): |
+ return [] |
+ return self.scan(node, path) |
+ |
+ kw['function'] = _scan |
+ kw['path_function'] = FindPathDirs(path_variable) |
+ kw['recursive'] = 1 |
+ kw['skeys'] = suffixes |
+ kw['name'] = name |
+ |
+ Current.__init__(self, *args, **kw) |
+ |
+ def find_include(self, include, source_dir, path): |
+ n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) |
+ return n, include |
+ |
+ def sort_key(self, include): |
+ return SCons.Node.FS._my_normcase(include) |
+ |
+ def find_include_names(self, node): |
+ return self.cre.findall(node.get_text_contents()) |
+ |
+ def scan(self, node, path=()): |
+ |
+ # cache the includes list in node so we only scan it once: |
+ if node.includes is not None: |
+ includes = node.includes |
+ else: |
+ includes = self.find_include_names (node) |
+ # Intern the names of the include files. Saves some memory |
+ # if the same header is included many times. |
+ node.includes = list(map(SCons.Util.silent_intern, includes)) |
+ |
+ # This is a hand-coded DSU (decorate-sort-undecorate, or |
+ # Schwartzian transform) pattern. The sort key is the raw name |
+ # of the file as specifed on the #include line (including the |
+ # " or <, since that may affect what file is found), which lets |
+ # us keep the sort order constant regardless of whether the file |
+ # is actually found in a Repository or locally. |
+ nodes = [] |
+ source_dir = node.get_dir() |
+ if callable(path): |
+ path = path() |
+ for include in includes: |
+ n, i = self.find_include(include, source_dir, path) |
+ |
+ if n is None: |
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning, |
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) |
+ else: |
+ nodes.append((self.sort_key(include), n)) |
+ |
+ return [pair[1] for pair in sorted(nodes)] |
+ |
+class ClassicCPP(Classic): |
+ """ |
+ A Classic Scanner subclass which takes into account the type of |
+ bracketing used to include the file, and uses classic CPP rules |
+ for searching for the files based on the bracketing. |
+ |
+ Note that in order for this to work, the regular expression passed |
+ to the constructor must return the leading bracket in group 0, and |
+ the contained filename in group 1. |
+ """ |
+ def find_include(self, include, source_dir, path): |
+ if include[0] == '"': |
+ paths = (source_dir,) + tuple(path) |
+ else: |
+ paths = tuple(path) + (source_dir,) |
+ |
+ n = SCons.Node.FS.find_file(include[1], paths) |
+ |
+ i = SCons.Util.silent_intern(include[1]) |
+ return n, i |
+ |
+ def sort_key(self, include): |
+ return SCons.Node.FS._my_normcase(' '.join(include)) |
+ |
+# Local Variables: |
+# tab-width:4 |
+# indent-tabs-mode:nil |
+# End: |
+# vim: set expandtab tabstop=4 shiftwidth=4: |
Property changes on: scons-2.0.1/engine/SCons/Scanner/__init__.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |