| Index: scons-2.0.1/engine/SCons/cpp.py
|
| ===================================================================
|
| --- scons-2.0.1/engine/SCons/cpp.py (revision 0)
|
| +++ scons-2.0.1/engine/SCons/cpp.py (revision 0)
|
| @@ -0,0 +1,589 @@
|
| +#
|
| +# 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/cpp.py 5134 2010/08/16 23:02:40 bdeegan"
|
| +
|
| +__doc__ = """
|
| +SCons C Pre-Processor module
|
| +"""
|
| +#TODO 2.3 and before has no sorted()
|
| +import SCons.compat
|
| +
|
| +import os
|
| +import re
|
| +
|
| +#
|
| +# First "subsystem" of regular expressions that we set up:
|
| +#
|
| +# Stuff to turn the C preprocessor directives in a file's contents into
|
| +# a list of tuples that we can process easily.
|
| +#
|
| +
|
| +# A table of regular expressions that fetch the arguments from the rest of
|
| +# a C preprocessor line. Different directives have different arguments
|
| +# that we want to fetch, using the regular expressions to which the lists
|
| +# of preprocessor directives map.
|
| +cpp_lines_dict = {
|
| + # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument,
|
| + # separated from the keyword by white space.
|
| + ('if', 'elif', 'ifdef', 'ifndef',)
|
| + : '\s+(.+)',
|
| +
|
| + # Fetch the rest of a #import/#include/#include_next line as one
|
| + # argument, with white space optional.
|
| + ('import', 'include', 'include_next',)
|
| + : '\s*(.+)',
|
| +
|
| + # We don't care what comes after a #else or #endif line.
|
| + ('else', 'endif',) : '',
|
| +
|
| + # Fetch three arguments from a #define line:
|
| + # 1) The #defined keyword.
|
| + # 2) The optional parentheses and arguments (if it's a function-like
|
| + # macro, '' if it's not).
|
| + # 3) The expansion value.
|
| + ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
|
| +
|
| + # Fetch the #undefed keyword from a #undef line.
|
| + ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)',
|
| +}
|
| +
|
| +# Create a table that maps each individual C preprocessor directive to
|
| +# the corresponding compiled regular expression that fetches the arguments
|
| +# we care about.
|
| +Table = {}
|
| +for op_list, expr in cpp_lines_dict.items():
|
| + e = re.compile(expr)
|
| + for op in op_list:
|
| + Table[op] = e
|
| +del e
|
| +del op
|
| +del op_list
|
| +
|
| +# Create a list of the expressions we'll use to match all of the
|
| +# preprocessor directives. These are the same as the directives
|
| +# themselves *except* that we must use a negative lookahead assertion
|
| +# when matching "if" so it doesn't match the "if" in "ifdef."
|
| +override = {
|
| + 'if' : 'if(?!def)',
|
| +}
|
| +l = [override.get(x, x) for x in Table.keys()]
|
| +
|
| +
|
| +# Turn the list of expressions into one big honkin' regular expression
|
| +# that will match all the preprocessor lines at once. This will return
|
| +# a list of tuples, one for each preprocessor line. The preprocessor
|
| +# directive will be the first element in each tuple, and the rest of
|
| +# the line will be the second element.
|
| +e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
|
| +
|
| +# And last but not least, compile the expression.
|
| +CPP_Expression = re.compile(e, re.M)
|
| +
|
| +
|
| +
|
| +
|
| +#
|
| +# Second "subsystem" of regular expressions that we set up:
|
| +#
|
| +# Stuff to translate a C preprocessor expression (as found on a #if or
|
| +# #elif line) into an equivalent Python expression that we can eval().
|
| +#
|
| +
|
| +# A dictionary that maps the C representation of Boolean operators
|
| +# to their Python equivalents.
|
| +CPP_to_Python_Ops_Dict = {
|
| + '!' : ' not ',
|
| + '!=' : ' != ',
|
| + '&&' : ' and ',
|
| + '||' : ' or ',
|
| + '?' : ' and ',
|
| + ':' : ' or ',
|
| + '\r' : '',
|
| +}
|
| +
|
| +CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)]
|
| +
|
| +# We have to sort the keys by length so that longer expressions
|
| +# come *before* shorter expressions--in particular, "!=" must
|
| +# come before "!" in the alternation. Without this, the Python
|
| +# re module, as late as version 2.2.2, empirically matches the
|
| +# "!" in "!=" first, instead of finding the longest match.
|
| +# What's up with that?
|
| +l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True)
|
| +
|
| +# Turn the list of keys into one regular expression that will allow us
|
| +# to substitute all of the operators at once.
|
| +expr = '|'.join(map(re.escape, l))
|
| +
|
| +# ...and compile the expression.
|
| +CPP_to_Python_Ops_Expression = re.compile(expr)
|
| +
|
| +# A separate list of expressions to be evaluated and substituted
|
| +# sequentially, not all at once.
|
| +CPP_to_Python_Eval_List = [
|
| + ['defined\s+(\w+)', '"\\1" in __dict__'],
|
| + ['defined\s*\((\w+)\)', '"\\1" in __dict__'],
|
| + ['/\*.*\*/', ''],
|
| + ['/\*.*', ''],
|
| + ['//.*', ''],
|
| + ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'],
|
| +]
|
| +
|
| +# Replace the string representations of the regular expressions in the
|
| +# list with compiled versions.
|
| +for l in CPP_to_Python_Eval_List:
|
| + l[0] = re.compile(l[0])
|
| +
|
| +# Wrap up all of the above into a handy function.
|
| +def CPP_to_Python(s):
|
| + """
|
| + Converts a C pre-processor expression into an equivalent
|
| + Python expression that can be evaluated.
|
| + """
|
| + s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s)
|
| + for expr, repl in CPP_to_Python_Eval_List:
|
| + s = expr.sub(repl, s)
|
| + return s
|
| +
|
| +
|
| +
|
| +del expr
|
| +del l
|
| +del override
|
| +
|
| +
|
| +
|
| +class FunctionEvaluator(object):
|
| + """
|
| + Handles delayed evaluation of a #define function call.
|
| + """
|
| + def __init__(self, name, args, expansion):
|
| + """
|
| + Squirrels away the arguments and expansion value of a #define
|
| + macro function for later evaluation when we must actually expand
|
| + a value that uses it.
|
| + """
|
| + self.name = name
|
| + self.args = function_arg_separator.split(args)
|
| + try:
|
| + expansion = expansion.split('##')
|
| + except AttributeError:
|
| + pass
|
| + self.expansion = expansion
|
| + def __call__(self, *values):
|
| + """
|
| + Evaluates the expansion of a #define macro function called
|
| + with the specified values.
|
| + """
|
| + if len(self.args) != len(values):
|
| + raise ValueError("Incorrect number of arguments to `%s'" % self.name)
|
| + # Create a dictionary that maps the macro arguments to the
|
| + # corresponding values in this "call." We'll use this when we
|
| + # eval() the expansion so that arguments will get expanded to
|
| + # the right values.
|
| + locals = {}
|
| + for k, v in zip(self.args, values):
|
| + locals[k] = v
|
| +
|
| + parts = []
|
| + for s in self.expansion:
|
| + if not s in self.args:
|
| + s = repr(s)
|
| + parts.append(s)
|
| + statement = ' + '.join(parts)
|
| +
|
| + return eval(statement, globals(), locals)
|
| +
|
| +
|
| +
|
| +# Find line continuations.
|
| +line_continuations = re.compile('\\\\\r?\n')
|
| +
|
| +# Search for a "function call" macro on an expansion. Returns the
|
| +# two-tuple of the "function" name itself, and a string containing the
|
| +# arguments within the call parentheses.
|
| +function_name = re.compile('(\S+)\(([^)]*)\)')
|
| +
|
| +# Split a string containing comma-separated function call arguments into
|
| +# the separate arguments.
|
| +function_arg_separator = re.compile(',\s*')
|
| +
|
| +
|
| +
|
| +class PreProcessor(object):
|
| + """
|
| + The main workhorse class for handling C pre-processing.
|
| + """
|
| + def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
|
| + global Table
|
| +
|
| + cpppath = tuple(cpppath)
|
| +
|
| + self.searchpath = {
|
| + '"' : (current,) + cpppath,
|
| + '<' : cpppath + (current,),
|
| + }
|
| +
|
| + # Initialize our C preprocessor namespace for tracking the
|
| + # values of #defined keywords. We use this namespace to look
|
| + # for keywords on #ifdef/#ifndef lines, and to eval() the
|
| + # expressions on #if/#elif lines (after massaging them from C to
|
| + # Python).
|
| + self.cpp_namespace = dict.copy()
|
| + self.cpp_namespace['__dict__'] = self.cpp_namespace
|
| +
|
| + if all:
|
| + self.do_include = self.all_include
|
| +
|
| + # For efficiency, a dispatch table maps each C preprocessor
|
| + # directive (#if, #define, etc.) to the method that should be
|
| + # called when we see it. We accomodate state changes (#if,
|
| + # #ifdef, #ifndef) by pushing the current dispatch table on a
|
| + # stack and changing what method gets called for each relevant
|
| + # directive we might see next at this level (#else, #elif).
|
| + # #endif will simply pop the stack.
|
| + d = {
|
| + 'scons_current_file' : self.scons_current_file
|
| + }
|
| + for op in Table.keys():
|
| + d[op] = getattr(self, 'do_' + op)
|
| + self.default_table = d
|
| +
|
| + # Controlling methods.
|
| +
|
| + def tupleize(self, contents):
|
| + """
|
| + Turns the contents of a file into a list of easily-processed
|
| + tuples describing the CPP lines in the file.
|
| +
|
| + The first element of each tuple is the line's preprocessor
|
| + directive (#if, #include, #define, etc., minus the initial '#').
|
| + The remaining elements are specific to the type of directive, as
|
| + pulled apart by the regular expression.
|
| + """
|
| + global CPP_Expression, Table
|
| + contents = line_continuations.sub('', contents)
|
| + cpp_tuples = CPP_Expression.findall(contents)
|
| + return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples]
|
| +
|
| + def __call__(self, file):
|
| + """
|
| + Pre-processes a file.
|
| +
|
| + This is the main public entry point.
|
| + """
|
| + self.current_file = file
|
| + return self.process_contents(self.read_file(file), file)
|
| +
|
| + def process_contents(self, contents, fname=None):
|
| + """
|
| + Pre-processes a file contents.
|
| +
|
| + This is the main internal entry point.
|
| + """
|
| + self.stack = []
|
| + self.dispatch_table = self.default_table.copy()
|
| + self.current_file = fname
|
| + self.tuples = self.tupleize(contents)
|
| +
|
| + self.initialize_result(fname)
|
| + while self.tuples:
|
| + t = self.tuples.pop(0)
|
| + # Uncomment to see the list of tuples being processed (e.g.,
|
| + # to validate the CPP lines are being translated correctly).
|
| + #print t
|
| + self.dispatch_table[t[0]](t)
|
| + return self.finalize_result(fname)
|
| +
|
| + # Dispatch table stack manipulation methods.
|
| +
|
| + def save(self):
|
| + """
|
| + Pushes the current dispatch table on the stack and re-initializes
|
| + the current dispatch table to the default.
|
| + """
|
| + self.stack.append(self.dispatch_table)
|
| + self.dispatch_table = self.default_table.copy()
|
| +
|
| + def restore(self):
|
| + """
|
| + Pops the previous dispatch table off the stack and makes it the
|
| + current one.
|
| + """
|
| + try: self.dispatch_table = self.stack.pop()
|
| + except IndexError: pass
|
| +
|
| + # Utility methods.
|
| +
|
| + def do_nothing(self, t):
|
| + """
|
| + Null method for when we explicitly want the action for a
|
| + specific preprocessor directive to do nothing.
|
| + """
|
| + pass
|
| +
|
| + def scons_current_file(self, t):
|
| + self.current_file = t[1]
|
| +
|
| + def eval_expression(self, t):
|
| + """
|
| + Evaluates a C preprocessor expression.
|
| +
|
| + This is done by converting it to a Python equivalent and
|
| + eval()ing it in the C preprocessor namespace we use to
|
| + track #define values.
|
| + """
|
| + t = CPP_to_Python(' '.join(t[1:]))
|
| + try: return eval(t, self.cpp_namespace)
|
| + except (NameError, TypeError): return 0
|
| +
|
| + def initialize_result(self, fname):
|
| + self.result = [fname]
|
| +
|
| + def finalize_result(self, fname):
|
| + return self.result[1:]
|
| +
|
| + def find_include_file(self, t):
|
| + """
|
| + Finds the #include file for a given preprocessor tuple.
|
| + """
|
| + fname = t[2]
|
| + for d in self.searchpath[t[1]]:
|
| + if d == os.curdir:
|
| + f = fname
|
| + else:
|
| + f = os.path.join(d, fname)
|
| + if os.path.isfile(f):
|
| + return f
|
| + return None
|
| +
|
| + def read_file(self, file):
|
| + return open(file).read()
|
| +
|
| + # Start and stop processing include lines.
|
| +
|
| + def start_handling_includes(self, t=None):
|
| + """
|
| + Causes the PreProcessor object to start processing #import,
|
| + #include and #include_next lines.
|
| +
|
| + This method will be called when a #if, #ifdef, #ifndef or #elif
|
| + evaluates True, or when we reach the #else in a #if, #ifdef,
|
| + #ifndef or #elif block where a condition already evaluated
|
| + False.
|
| +
|
| + """
|
| + d = self.dispatch_table
|
| + d['import'] = self.do_import
|
| + d['include'] = self.do_include
|
| + d['include_next'] = self.do_include
|
| +
|
| + def stop_handling_includes(self, t=None):
|
| + """
|
| + Causes the PreProcessor object to stop processing #import,
|
| + #include and #include_next lines.
|
| +
|
| + This method will be called when a #if, #ifdef, #ifndef or #elif
|
| + evaluates False, or when we reach the #else in a #if, #ifdef,
|
| + #ifndef or #elif block where a condition already evaluated True.
|
| + """
|
| + d = self.dispatch_table
|
| + d['import'] = self.do_nothing
|
| + d['include'] = self.do_nothing
|
| + d['include_next'] = self.do_nothing
|
| +
|
| + # Default methods for handling all of the preprocessor directives.
|
| + # (Note that what actually gets called for a given directive at any
|
| + # point in time is really controlled by the dispatch_table.)
|
| +
|
| + def _do_if_else_condition(self, condition):
|
| + """
|
| + Common logic for evaluating the conditions on #if, #ifdef and
|
| + #ifndef lines.
|
| + """
|
| + self.save()
|
| + d = self.dispatch_table
|
| + if condition:
|
| + self.start_handling_includes()
|
| + d['elif'] = self.stop_handling_includes
|
| + d['else'] = self.stop_handling_includes
|
| + else:
|
| + self.stop_handling_includes()
|
| + d['elif'] = self.do_elif
|
| + d['else'] = self.start_handling_includes
|
| +
|
| + def do_ifdef(self, t):
|
| + """
|
| + Default handling of a #ifdef line.
|
| + """
|
| + self._do_if_else_condition(t[1] in self.cpp_namespace)
|
| +
|
| + def do_ifndef(self, t):
|
| + """
|
| + Default handling of a #ifndef line.
|
| + """
|
| + self._do_if_else_condition(t[1] not in self.cpp_namespace)
|
| +
|
| + def do_if(self, t):
|
| + """
|
| + Default handling of a #if line.
|
| + """
|
| + self._do_if_else_condition(self.eval_expression(t))
|
| +
|
| + def do_elif(self, t):
|
| + """
|
| + Default handling of a #elif line.
|
| + """
|
| + d = self.dispatch_table
|
| + if self.eval_expression(t):
|
| + self.start_handling_includes()
|
| + d['elif'] = self.stop_handling_includes
|
| + d['else'] = self.stop_handling_includes
|
| +
|
| + def do_else(self, t):
|
| + """
|
| + Default handling of a #else line.
|
| + """
|
| + pass
|
| +
|
| + def do_endif(self, t):
|
| + """
|
| + Default handling of a #endif line.
|
| + """
|
| + self.restore()
|
| +
|
| + def do_define(self, t):
|
| + """
|
| + Default handling of a #define line.
|
| + """
|
| + _, name, args, expansion = t
|
| + try:
|
| + expansion = int(expansion)
|
| + except (TypeError, ValueError):
|
| + pass
|
| + if args:
|
| + evaluator = FunctionEvaluator(name, args[1:-1], expansion)
|
| + self.cpp_namespace[name] = evaluator
|
| + else:
|
| + self.cpp_namespace[name] = expansion
|
| +
|
| + def do_undef(self, t):
|
| + """
|
| + Default handling of a #undef line.
|
| + """
|
| + try: del self.cpp_namespace[t[1]]
|
| + except KeyError: pass
|
| +
|
| + def do_import(self, t):
|
| + """
|
| + Default handling of a #import line.
|
| + """
|
| + # XXX finish this -- maybe borrow/share logic from do_include()...?
|
| + pass
|
| +
|
| + def do_include(self, t):
|
| + """
|
| + Default handling of a #include line.
|
| + """
|
| + t = self.resolve_include(t)
|
| + include_file = self.find_include_file(t)
|
| + if include_file:
|
| + #print "include_file =", include_file
|
| + self.result.append(include_file)
|
| + contents = self.read_file(include_file)
|
| + new_tuples = [('scons_current_file', include_file)] + \
|
| + self.tupleize(contents) + \
|
| + [('scons_current_file', self.current_file)]
|
| + self.tuples[:] = new_tuples + self.tuples
|
| +
|
| + # Date: Tue, 22 Nov 2005 20:26:09 -0500
|
| + # From: Stefan Seefeld <seefeld@sympatico.ca>
|
| + #
|
| + # By the way, #include_next is not the same as #include. The difference
|
| + # being that #include_next starts its search in the path following the
|
| + # path that let to the including file. In other words, if your system
|
| + # include paths are ['/foo', '/bar'], and you are looking at a header
|
| + # '/foo/baz.h', it might issue an '#include_next <baz.h>' which would
|
| + # correctly resolve to '/bar/baz.h' (if that exists), but *not* see
|
| + # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html
|
| + # for more reasoning.
|
| + #
|
| + # I have no idea in what context 'import' might be used.
|
| +
|
| + # XXX is #include_next really the same as #include ?
|
| + do_include_next = do_include
|
| +
|
| + # Utility methods for handling resolution of include files.
|
| +
|
| + def resolve_include(self, t):
|
| + """Resolve a tuple-ized #include line.
|
| +
|
| + This handles recursive expansion of values without "" or <>
|
| + surrounding the name until an initial " or < is found, to handle
|
| + #include FILE
|
| + where FILE is a #define somewhere else.
|
| + """
|
| + s = t[1]
|
| + while not s[0] in '<"':
|
| + #print "s =", s
|
| + try:
|
| + s = self.cpp_namespace[s]
|
| + except KeyError:
|
| + m = function_name.search(s)
|
| + s = self.cpp_namespace[m.group(1)]
|
| + if callable(s):
|
| + args = function_arg_separator.split(m.group(2))
|
| + s = s(*args)
|
| + if not s:
|
| + return None
|
| + return (t[0], s[0], s[1:-1])
|
| +
|
| + def all_include(self, t):
|
| + """
|
| + """
|
| + self.result.append(self.resolve_include(t))
|
| +
|
| +class DumbPreProcessor(PreProcessor):
|
| + """A preprocessor that ignores all #if/#elif/#else/#endif directives
|
| + and just reports back *all* of the #include files (like the classic
|
| + SCons scanner did).
|
| +
|
| + This is functionally equivalent to using a regular expression to
|
| + find all of the #include lines, only slower. It exists mainly as
|
| + an example of how the main PreProcessor class can be sub-classed
|
| + to tailor its behavior.
|
| + """
|
| + def __init__(self, *args, **kw):
|
| + PreProcessor.__init__(self, *args, **kw)
|
| + d = self.default_table
|
| + for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
|
| + d[func] = d[func] = self.do_nothing
|
| +
|
| +del __revision__
|
| +
|
| +# 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/cpp.py
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|