summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/cpp.py
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2006-11-02 16:21:36 (GMT)
committerSteven Knight <knight@baldmt.com>2006-11-02 16:21:36 (GMT)
commit6fcc7d78858a5ca068e3deea183d35295b7dd88e (patch)
treeea835f8b96e6c67766b4c0e6437f363d76164d39 /src/engine/SCons/cpp.py
parent66edd5ceedb4e6b83e6b4d03e244af74fe720041 (diff)
downloadSCons-6fcc7d78858a5ca068e3deea183d35295b7dd88e.zip
SCons-6fcc7d78858a5ca068e3deea183d35295b7dd88e.tar.gz
SCons-6fcc7d78858a5ca068e3deea183d35295b7dd88e.tar.bz2
Merged revisions 1582-1665 via svnmerge from
http://scons.tigris.org/svn/scons/branches/core ........ r1585 | stevenknight | 2006-08-06 21:21:12 -0500 (Sun, 06 Aug 2006) | 1 line 0.96.D430 - Fix bug with finding Fortran modules in build directories. (Nicolas Vigier) ........ r1586 | stevenknight | 2006-08-06 22:54:39 -0500 (Sun, 06 Aug 2006) | 1 line 0.96.D431 - Fix use of BuildDir when the source file is a relative-path symlink. (Nicola Vi ........ r1587 | timot | 2006-08-10 14:45:00 -0500 (Thu, 10 Aug 2006) | 1 line fix Platform SDK init ........ r1589 | stevenknight | 2006-08-12 13:28:51 -0500 (Sat, 12 Aug 2006) | 1 line 0.96.D432 - Change the default mingw linker from g++ to gcc. (Mariusz Olko) ........ r1590 | stevenknight | 2006-08-13 11:16:32 -0500 (Sun, 13 Aug 2006) | 1 line 0.96.D433 - More runtest.py enhancements. ........ r1594 | stevenknight | 2006-08-15 04:47:46 -0500 (Tue, 15 Aug 2006) | 1 line 0.96.D434 - Print the full path of SConscript files in stack traces. (Dobes Vandermeer) ........ r1600 | timot | 2006-08-16 11:34:44 -0500 (Wed, 16 Aug 2006) | 1 line add M4 to mingw tools ........ r1611 | stevenknight | 2006-08-19 16:25:24 -0500 (Sat, 19 Aug 2006) | 1 line 0.96.D435 - Add an SCons XMLResultStream to capture test results for mailing in. Get Aegis ........ r1617 | timot | 2006-08-21 16:19:03 -0500 (Mon, 21 Aug 2006) | 1 line handling of spawnve returning an error code that is not in exitvalmap ........ r1619 | stevenknight | 2006-09-01 19:07:47 -0500 (Fri, 01 Sep 2006) | 1 line 0.96.D436 - Win32 test portability and other test fixes. ........ r1620 | stevenknight | 2006-09-02 20:21:51 -0500 (Sat, 02 Sep 2006) | 2 lines Bring msvc.py in sync with Aegis repository. ........ r1621 | stevenknight | 2006-09-02 20:23:48 -0500 (Sat, 02 Sep 2006) | 2 lines Move to keep symlink tests together. ........ r1622 | garyo | 2006-09-06 11:51:42 -0500 (Wed, 06 Sep 2006) | 1 line Fix for Issue #1370; allow exit values not in exitvalmap. Added some tests for this kind of thing. Also improved win32 err msg if command exits with nonzero to show actual cmd, not just "cmd.exe". Note this fix improves posix and win32 behavior. ........ r1623 | stevenknight | 2006-09-07 06:35:16 -0500 (Thu, 07 Sep 2006) | 1 line 0.96.D440 - Fix runtest.py with QMTest on Windows. ........ r1625 | stevenknight | 2006-09-09 09:22:15 -0500 (Sat, 09 Sep 2006) | 3 lines Comment out a long-command test which fails on older Pythons (1.5.2) on Fedora Core 3. We can restore it in the future. ........ r1626 | stevenknight | 2006-09-09 16:17:44 -0500 (Sat, 09 Sep 2006) | 1 line 0.96.D441 - Allow Python Values to be the targets of Builders. (Anonymous) ........ r1627 | stevenknight | 2006-09-09 20:25:53 -0500 (Sat, 09 Sep 2006) | 1 line 0.96.D442 - Support src_dir on SConscript() calls. (Dobes Vandermeer) ........ r1628 | stevenknight | 2006-09-10 07:28:54 -0500 (Sun, 10 Sep 2006) | 1 line 0.96.D443 - Add miscellaneous utility scripts and config changes. ........ r1629 | stevenknight | 2006-09-11 04:45:01 -0500 (Mon, 11 Sep 2006) | 1 line 0.96.D444 - Add a test case for BuildDir handling of nested SConscript files. (Adam Simpkin ........ r1630 | stevenknight | 2006-09-11 11:34:07 -0500 (Mon, 11 Sep 2006) | 1 line 0.96.D445 - Workaround bug in early versions of thePython 2.4 profiler. ........ r1631 | stevenknight | 2006-09-19 19:12:51 -0500 (Tue, 19 Sep 2006) | 1 line 0.96.D446 - Fix Visual Studio common prefix handling to only treat common prefixes on comple ........ r1632 | stevenknight | 2006-09-25 07:11:44 -0500 (Mon, 25 Sep 2006) | 1 line 0.96.D447 - Fix tests that fail due to warnings from (some versions?) of gcc. (Sohail Soman ........ r1633 | stevenknight | 2006-09-25 07:57:48 -0500 (Mon, 25 Sep 2006) | 1 line 0.96.D448 - Handle python paths with quotes in tests. ........ r1634 | stevenknight | 2006-09-25 14:38:07 -0500 (Mon, 25 Sep 2006) | 1 line 0.96.D449 - Fix SCons build when python is not in the path (e.g. on Windows). (Chad Austin) ........ r1635 | stevenknight | 2006-09-26 11:28:23 -0500 (Tue, 26 Sep 2006) | 1 line 0.96.D450 - Handle warnings from Python 2.1; make sure we still test on Python 1.5. ........ r1636 | stevenknight | 2006-09-27 05:34:23 -0500 (Wed, 27 Sep 2006) | 1 line 0.96.D451 - Avoid calling Options validators and converters twice. ........ r1637 | stevenknight | 2006-09-28 08:12:38 -0500 (Thu, 28 Sep 2006) | 1 line 0.96.D452 - Allow setting MSVS_VERSION after initialization to select the Visual Studio vers ........ r1638 | stevenknight | 2006-09-30 08:38:15 -0500 (Sat, 30 Sep 2006) | 1 line 0.96.D453 - Give the MSVC resource builder a src_builder list and .rc src_suffix. (Leanid N ........ r1639 | stevenknight | 2006-10-12 08:50:58 -0500 (Thu, 12 Oct 2006) | 1 line 0.96.D454 - Test handling of env.Append() and env.Prepend(), making sure it works on later P ........ r1640 | stevenknight | 2006-10-15 20:42:09 -0500 (Sun, 15 Oct 2006) | 1 line 0.96.D455 - Support the runtest.py -f option when using QMTest. ........ r1641 | stevenknight | 2006-10-15 21:20:02 -0500 (Sun, 15 Oct 2006) | 1 line 0.96.D456 - Fix an error in ListOption handling caused by making new copies of Options objec ........ r1642 | stevenknight | 2006-10-16 05:53:14 -0500 (Mon, 16 Oct 2006) | 1 line 0.96.D457 - Fix new Append()/Prepend() handling of dictionaries in later Python versions (2. ........ r1643 | stevenknight | 2006-10-16 07:13:16 -0500 (Mon, 16 Oct 2006) | 1 line 0.96.D458 - Allow Install() to handle directories as sources. (Matthew A. Nicholson) ........ r1644 | stevenknight | 2006-10-17 09:17:58 -0500 (Tue, 17 Oct 2006) | 1 line 0.96.D459 - Add a test to make sure SideEffect() doesn't interfere with CacheDir(). Refacto ........ r1645 | stevenknight | 2006-10-17 10:20:22 -0500 (Tue, 17 Oct 2006) | 1 line 0.96.D460 - Do not use -fPIC when using gcc on win32 (MinGW). (Jan Nijtmans) ........ r1646 | stevenknight | 2006-10-17 17:21:58 -0500 (Tue, 17 Oct 2006) | 6 lines Move all the scons.org stuff from the scons source tree itself to a directory next to the trunk, and delete the copies from the branches. There's a lot of stuff there (what with all of the documentation of the different versions) and it's ridiculous to make everyone sync it just to work on the code. ........ r1647 | stevenknight | 2006-10-17 23:18:29 -0500 (Tue, 17 Oct 2006) | 1 line 0.96.D461 - Fix the tests of runtest.py so they skip appropriately if qmtest.py isn't instal ........ r1648 | stevenknight | 2006-10-18 08:48:47 -0500 (Wed, 18 Oct 2006) | 1 line 0.96.D462 - When using --implicit-cache, do not re-scan files if the scanner returned no imp ........ r1649 | stevenknight | 2006-10-18 19:42:13 -0500 (Wed, 18 Oct 2006) | 1 line 0.96.D463 - More test portability fixes. ........ r1650 | stevenknight | 2006-10-19 00:30:23 -0500 (Thu, 19 Oct 2006) | 1 line 0.96.D464 - Add a cpp.py module that knows how to find dependencies from #include lines like ........ r1651 | stevenknight | 2006-10-20 06:49:51 -0500 (Fri, 20 Oct 2006) | 1 line 0.96.D465 - Fix unresolved variable name in win32 portion of test. ........ r1652 | stevenknight | 2006-10-23 00:20:38 -0500 (Mon, 23 Oct 2006) | 1 line 0.96.D466 - Add an option for tracing files to and from the CacheDir. ........ r1653 | stevenknight | 2006-10-23 00:29:32 -0500 (Mon, 23 Oct 2006) | 1 line 0.96.D467 - Make {Append,Prepend}Unique() handle adding elements to empty lists like {Append ........ r1654 | stevenknight | 2006-10-23 02:38:06 -0500 (Mon, 23 Oct 2006) | 1 line 0.96.D468 - Allow Debug.caller() to take multiple arguments; add a debug utility to post-pro ........ r1655 | stevenknight | 2006-10-23 03:16:42 -0500 (Mon, 23 Oct 2006) | 1 line 0.96.D469 - Reduce unnecessary calls to Node.FS.disambiguate(), undoing (?) a performance hi ........ r1656 | stevenknight | 2006-10-25 00:06:27 -0500 (Wed, 25 Oct 2006) | 1 line 0.96.D470 - More test portability fixes. ........ r1657 | stevenknight | 2006-10-25 00:16:22 -0500 (Wed, 25 Oct 2006) | 1 line 0.96.D471 - Have runtest.py fall back to the --noqmtest option (with a warning) if qmtest.py ........ r1658 | stevenknight | 2006-10-25 12:12:02 -0500 (Wed, 25 Oct 2006) | 1 line 0.96.D472 - Document the default use of the /Z7 flag for Visual Studio and ways to use /Zi. ........ r1659 | stevenknight | 2006-10-26 23:53:51 -0500 (Thu, 26 Oct 2006) | 1 line 0.96.D473 - Have runtest.py -d accomodate different Python library locations. ........ r1660 | stevenknight | 2006-10-27 00:03:59 -0500 (Fri, 27 Oct 2006) | 1 line 0.96.D474 - Patch to support running SCons under WingIDE. (Allen Bierbaum) ........ r1661 | stevenknight | 2006-10-27 12:17:27 -0500 (Fri, 27 Oct 2006) | 1 line 0.96.D475 - Restore execution of all Environment unit tests. ........ r1662 | stevenknight | 2006-10-31 23:22:58 -0600 (Tue, 31 Oct 2006) | 1 line 0.96.D476 - Eliminate unnecessary print from a test, left over from debugging. ........ r1663 | stevenknight | 2006-10-31 23:32:00 -0600 (Tue, 31 Oct 2006) | 1 line 0.96.D477 - Support creating shared object files from assembly language. (James Y. Knight) ........ r1664 | stevenknight | 2006-10-31 23:44:08 -0600 (Tue, 31 Oct 2006) | 1 line 0.96.D478 - Fix the Memoizer to deal with a compiled .pyo or .pyc file that's in a different ........ r1665 | stevenknight | 2006-11-01 21:59:18 -0600 (Wed, 01 Nov 2006) | 1 line 0.96.D479 - Put back the scons-{LICENSE,README} files in the scons-loacal package; add tests ........
Diffstat (limited to 'src/engine/SCons/cpp.py')
-rw-r--r--src/engine/SCons/cpp.py561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py
new file mode 100644
index 0000000..9bf5779
--- /dev/null
+++ b/src/engine/SCons/cpp.py
@@ -0,0 +1,561 @@
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+__doc__ = """
+SCons C Pre-Processor module
+"""
+
+import os
+import re
+import string
+import sys
+
+
+
+import __builtin__
+try:
+ __builtin__.zip
+except AttributeError:
+ def zip(*lists):
+ result = []
+ for i in xrange(len(lists[0])):
+ result.append(tuple(map(lambda l, i=i: l[i], lists)))
+ return result
+
+
+
+#
+# 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/#import/#include/
+ # #include_next line as one argument.
+ ('if', 'elif', 'ifdef', 'ifndef', '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 = map(lambda x, o=override: o.get(x, x), 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*(' + string.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, d=CPP_to_Python_Ops_Dict: d[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 = CPP_to_Python_Ops_Dict.keys()
+l.sort(lambda a, b: cmp(len(b), len(a)))
+
+# Turn the list of keys into one regular expression that will allow us
+# to substitute all of the operators at once.
+expr = string.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+)', '__dict__.has_key("\\1")'],
+ ['defined\s*\((\w+)\)', '__dict__.has_key("\\1")'],
+ ['/\*.*\*/', ''],
+ ['/\*.*', ''],
+ ['//.*', ''],
+ ['(0x[0-9A-Fa-f]*)[UL]+', '\\1L'],
+]
+
+# 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:
+ """
+ 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)
+ self.expansion = string.split(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 = string.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:
+ """
+ The main workhorse class for handling C pre-processing.
+ """
+ def __init__(self, current='.', cpppath=[], dict={}, all=0):
+ global Table
+
+ 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 = {}
+ 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 map(lambda m, t=Table:
+ (m[0],) + t[m[0]].match(m[1]).groups(),
+ cpp_tuples)
+
+ def __call__(self, contents):
+ """
+ Pre-processes a file contents.
+
+ This is the main entry point, which
+ """
+ self.stack = []
+ self.dispatch_table = self.default_table.copy()
+ self.tuples = self.tupleize(contents)
+
+ self.result = []
+ 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.result
+
+ # 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 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(string.join(t[1:]))
+ try: return eval(t, self.cpp_namespace)
+ except (NameError, TypeError): return 0
+
+ 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]]:
+ f = os.path.join(d, fname)
+ if os.path.isfile(f):
+ return f
+ return None
+
+ # 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(self.cpp_namespace.has_key(t[1]))
+
+ def do_ifndef(self, t):
+ """
+ Default handling of a #ifndef line.
+ """
+ self._do_if_else_condition(not self.cpp_namespace.has_key(t[1]))
+
+ 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 = open(include_file).read()
+ new_tuples = self.tupleize(contents)
+ 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 = apply(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):
+ apply(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__