1#
2# Copyright (c) 2001 - 2014 The SCons Foundation
3#
4# Permission is hereby granted, free of charge, to any person obtaining
5# a copy of this software and associated documentation files (the
6# "Software"), to deal in the Software without restriction, including
7# without limitation the rights to use, copy, modify, merge, publish,
8# distribute, sublicense, and/or sell copies of the Software, and to
9# permit persons to whom the Software is furnished to do so, subject to
10# the following conditions:
11#
12# The above copyright notice and this permission notice shall be included
13# in all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22#
23
24__revision__ = "src/engine/SCons/cpp.py  2014/07/05 09:42:21 garyo"
25
26__doc__ = """
27SCons C Pre-Processor module
28"""
29#TODO 2.3 and before has no sorted()
30import SCons.compat
31
32import os
33import re
34
35#
36# First "subsystem" of regular expressions that we set up:
37#
38# Stuff to turn the C preprocessor directives in a file's contents into
39# a list of tuples that we can process easily.
40#
41
42# A table of regular expressions that fetch the arguments from the rest of
43# a C preprocessor line.  Different directives have different arguments
44# that we want to fetch, using the regular expressions to which the lists
45# of preprocessor directives map.
46cpp_lines_dict = {
47    # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument,
48    # separated from the keyword by white space.
49    ('if', 'elif', 'ifdef', 'ifndef',)
50                        : '\s+(.+)',
51
52    # Fetch the rest of a #import/#include/#include_next line as one
53    # argument, with white space optional.
54    ('import', 'include', 'include_next',)
55                        : '\s*(.+)',
56
57    # We don't care what comes after a #else or #endif line.
58    ('else', 'endif',)  : '',
59
60    # Fetch three arguments from a #define line:
61    #   1) The #defined keyword.
62    #   2) The optional parentheses and arguments (if it's a function-like
63    #      macro, '' if it's not).
64    #   3) The expansion value.
65    ('define',)         : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
66
67    # Fetch the #undefed keyword from a #undef line.
68    ('undef',)          : '\s+([_A-Za-z][A-Za-z0-9_]*)',
69}
70
71# Create a table that maps each individual C preprocessor directive to
72# the corresponding compiled regular expression that fetches the arguments
73# we care about.
74Table = {}
75for op_list, expr in cpp_lines_dict.items():
76    e = re.compile(expr)
77    for op in op_list:
78        Table[op] = e
79del e
80del op
81del op_list
82
83# Create a list of the expressions we'll use to match all of the
84# preprocessor directives.  These are the same as the directives
85# themselves *except* that we must use a negative lookahead assertion
86# when matching "if" so it doesn't match the "if" in "ifdef."
87override = {
88    'if'                        : 'if(?!def)',
89}
90l = [override.get(x, x) for x in Table.keys()]
91
92
93# Turn the list of expressions into one big honkin' regular expression
94# that will match all the preprocessor lines at once.  This will return
95# a list of tuples, one for each preprocessor line.  The preprocessor
96# directive will be the first element in each tuple, and the rest of
97# the line will be the second element.
98e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
99
100# And last but not least, compile the expression.
101CPP_Expression = re.compile(e, re.M)
102
103
104
105
106#
107# Second "subsystem" of regular expressions that we set up:
108#
109# Stuff to translate a C preprocessor expression (as found on a #if or
110# #elif line) into an equivalent Python expression that we can eval().
111#
112
113# A dictionary that maps the C representation of Boolean operators
114# to their Python equivalents.
115CPP_to_Python_Ops_Dict = {
116    '!'         : ' not ',
117    '!='        : ' != ',
118    '&&'        : ' and ',
119    '||'        : ' or ',
120    '?'         : ' and ',
121    ':'         : ' or ',
122    '\r'        : '',
123}
124
125CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)]
126
127# We have to sort the keys by length so that longer expressions
128# come *before* shorter expressions--in particular, "!=" must
129# come before "!" in the alternation.  Without this, the Python
130# re module, as late as version 2.2.2, empirically matches the
131# "!" in "!=" first, instead of finding the longest match.
132# What's up with that?
133l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True)
134
135# Turn the list of keys into one regular expression that will allow us
136# to substitute all of the operators at once.
137expr = '|'.join(map(re.escape, l))
138
139# ...and compile the expression.
140CPP_to_Python_Ops_Expression = re.compile(expr)
141
142# A separate list of expressions to be evaluated and substituted
143# sequentially, not all at once.
144CPP_to_Python_Eval_List = [
145    ['defined\s+(\w+)',         '"\\1" in __dict__'],
146    ['defined\s*\((\w+)\)',     '"\\1" in __dict__'],
147    ['/\*.*\*/',                ''],
148    ['/\*.*',                   ''],
149    ['//.*',                    ''],
150    ['(0x[0-9A-Fa-f]*)[UL]+',   '\\1'],
151]
152
153# Replace the string representations of the regular expressions in the
154# list with compiled versions.
155for l in CPP_to_Python_Eval_List:
156    l[0] = re.compile(l[0])
157
158# Wrap up all of the above into a handy function.
159def CPP_to_Python(s):
160    """
161    Converts a C pre-processor expression into an equivalent
162    Python expression that can be evaluated.
163    """
164    s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s)
165    for expr, repl in CPP_to_Python_Eval_List:
166        s = expr.sub(repl, s)
167    return s
168
169
170
171del expr
172del l
173del override
174
175
176
177class FunctionEvaluator(object):
178    """
179    Handles delayed evaluation of a #define function call.
180    """
181    def __init__(self, name, args, expansion):
182        """
183        Squirrels away the arguments and expansion value of a #define
184        macro function for later evaluation when we must actually expand
185        a value that uses it.
186        """
187        self.name = name
188        self.args = function_arg_separator.split(args)
189        try:
190            expansion = expansion.split('##')
191        except AttributeError:
192            pass
193        self.expansion = expansion
194    def __call__(self, *values):
195        """
196        Evaluates the expansion of a #define macro function called
197        with the specified values.
198        """
199        if len(self.args) != len(values):
200            raise ValueError("Incorrect number of arguments to `%s'" % self.name)
201        # Create a dictionary that maps the macro arguments to the
202        # corresponding values in this "call."  We'll use this when we
203        # eval() the expansion so that arguments will get expanded to
204        # the right values.
205        locals = {}
206        for k, v in zip(self.args, values):
207            locals[k] = v
208
209        parts = []
210        for s in self.expansion:
211            if not s in self.args:
212                s = repr(s)
213            parts.append(s)
214        statement = ' + '.join(parts)
215
216        return eval(statement, globals(), locals)
217
218
219
220# Find line continuations.
221line_continuations = re.compile('\\\\\r?\n')
222
223# Search for a "function call" macro on an expansion.  Returns the
224# two-tuple of the "function" name itself, and a string containing the
225# arguments within the call parentheses.
226function_name = re.compile('(\S+)\(([^)]*)\)')
227
228# Split a string containing comma-separated function call arguments into
229# the separate arguments.
230function_arg_separator = re.compile(',\s*')
231
232
233
234class PreProcessor(object):
235    """
236    The main workhorse class for handling C pre-processing.
237    """
238    def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
239        global Table
240
241        cpppath = tuple(cpppath)
242
243        self.searchpath = {
244            '"' :       (current,) + cpppath,
245            '<' :       cpppath + (current,),
246        }
247
248        # Initialize our C preprocessor namespace for tracking the
249        # values of #defined keywords.  We use this namespace to look
250        # for keywords on #ifdef/#ifndef lines, and to eval() the
251        # expressions on #if/#elif lines (after massaging them from C to
252        # Python).
253        self.cpp_namespace = dict.copy()
254        self.cpp_namespace['__dict__'] = self.cpp_namespace
255
256        if all:
257           self.do_include = self.all_include
258
259        # For efficiency, a dispatch table maps each C preprocessor
260        # directive (#if, #define, etc.) to the method that should be
261        # called when we see it.  We accomodate state changes (#if,
262        # #ifdef, #ifndef) by pushing the current dispatch table on a
263        # stack and changing what method gets called for each relevant
264        # directive we might see next at this level (#else, #elif).
265        # #endif will simply pop the stack.
266        d = {
267            'scons_current_file'    : self.scons_current_file
268        }
269        for op in Table.keys():
270            d[op] = getattr(self, 'do_' + op)
271        self.default_table = d
272
273    # Controlling methods.
274
275    def tupleize(self, contents):
276        """
277        Turns the contents of a file into a list of easily-processed
278        tuples describing the CPP lines in the file.
279
280        The first element of each tuple is the line's preprocessor
281        directive (#if, #include, #define, etc., minus the initial '#').
282        The remaining elements are specific to the type of directive, as
283        pulled apart by the regular expression.
284        """
285        global CPP_Expression, Table
286        contents = line_continuations.sub('', contents)
287        cpp_tuples = CPP_Expression.findall(contents)
288        return  [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples]
289
290    def __call__(self, file):
291        """
292        Pre-processes a file.
293
294        This is the main public entry point.
295        """
296        self.current_file = file
297        return self.process_contents(self.read_file(file), file)
298
299    def process_contents(self, contents, fname=None):
300        """
301        Pre-processes a file contents.
302
303        This is the main internal entry point.
304        """
305        self.stack = []
306        self.dispatch_table = self.default_table.copy()
307        self.current_file = fname
308        self.tuples = self.tupleize(contents)
309
310        self.initialize_result(fname)
311        while self.tuples:
312            t = self.tuples.pop(0)
313            # Uncomment to see the list of tuples being processed (e.g.,
314            # to validate the CPP lines are being translated correctly).
315            #print t
316            self.dispatch_table[t[0]](t)
317        return self.finalize_result(fname)
318
319    # Dispatch table stack manipulation methods.
320
321    def save(self):
322        """
323        Pushes the current dispatch table on the stack and re-initializes
324        the current dispatch table to the default.
325        """
326        self.stack.append(self.dispatch_table)
327        self.dispatch_table = self.default_table.copy()
328
329    def restore(self):
330        """
331        Pops the previous dispatch table off the stack and makes it the
332        current one.
333        """
334        try: self.dispatch_table = self.stack.pop()
335        except IndexError: pass
336
337    # Utility methods.
338
339    def do_nothing(self, t):
340        """
341        Null method for when we explicitly want the action for a
342        specific preprocessor directive to do nothing.
343        """
344        pass
345
346    def scons_current_file(self, t):
347        self.current_file = t[1]
348
349    def eval_expression(self, t):
350        """
351        Evaluates a C preprocessor expression.
352
353        This is done by converting it to a Python equivalent and
354        eval()ing it in the C preprocessor namespace we use to
355        track #define values.
356        """
357        t = CPP_to_Python(' '.join(t[1:]))
358        try: return eval(t, self.cpp_namespace)
359        except (NameError, TypeError): return 0
360
361    def initialize_result(self, fname):
362        self.result = [fname]
363
364    def finalize_result(self, fname):
365        return self.result[1:]
366
367    def find_include_file(self, t):
368        """
369        Finds the #include file for a given preprocessor tuple.
370        """
371        fname = t[2]
372        for d in self.searchpath[t[1]]:
373            if d == os.curdir:
374                f = fname
375            else:
376                f = os.path.join(d, fname)
377            if os.path.isfile(f):
378                return f
379        return None
380
381    def read_file(self, file):
382        return open(file).read()
383
384    # Start and stop processing include lines.
385
386    def start_handling_includes(self, t=None):
387        """
388        Causes the PreProcessor object to start processing #import,
389        #include and #include_next lines.
390
391        This method will be called when a #if, #ifdef, #ifndef or #elif
392        evaluates True, or when we reach the #else in a #if, #ifdef,
393        #ifndef or #elif block where a condition already evaluated
394        False.
395
396        """
397        d = self.dispatch_table
398        p = self.stack[-1] if self.stack else self.default_table
399
400        for k in ('import', 'include', 'include_next'):
401            d[k] = p[k]
402
403    def stop_handling_includes(self, t=None):
404        """
405        Causes the PreProcessor object to stop processing #import,
406        #include and #include_next lines.
407
408        This method will be called when a #if, #ifdef, #ifndef or #elif
409        evaluates False, or when we reach the #else in a #if, #ifdef,
410        #ifndef or #elif block where a condition already evaluated True.
411        """
412        d = self.dispatch_table
413        d['import'] = self.do_nothing
414        d['include'] =  self.do_nothing
415        d['include_next'] =  self.do_nothing
416
417    # Default methods for handling all of the preprocessor directives.
418    # (Note that what actually gets called for a given directive at any
419    # point in time is really controlled by the dispatch_table.)
420
421    def _do_if_else_condition(self, condition):
422        """
423        Common logic for evaluating the conditions on #if, #ifdef and
424        #ifndef lines.
425        """
426        self.save()
427        d = self.dispatch_table
428        if condition:
429            self.start_handling_includes()
430            d['elif'] = self.stop_handling_includes
431            d['else'] = self.stop_handling_includes
432        else:
433            self.stop_handling_includes()
434            d['elif'] = self.do_elif
435            d['else'] = self.start_handling_includes
436
437    def do_ifdef(self, t):
438        """
439        Default handling of a #ifdef line.
440        """
441        self._do_if_else_condition(t[1] in self.cpp_namespace)
442
443    def do_ifndef(self, t):
444        """
445        Default handling of a #ifndef line.
446        """
447        self._do_if_else_condition(t[1] not in self.cpp_namespace)
448
449    def do_if(self, t):
450        """
451        Default handling of a #if line.
452        """
453        self._do_if_else_condition(self.eval_expression(t))
454
455    def do_elif(self, t):
456        """
457        Default handling of a #elif line.
458        """
459        d = self.dispatch_table
460        if self.eval_expression(t):
461            self.start_handling_includes()
462            d['elif'] = self.stop_handling_includes
463            d['else'] = self.stop_handling_includes
464
465    def do_else(self, t):
466        """
467        Default handling of a #else line.
468        """
469        pass
470
471    def do_endif(self, t):
472        """
473        Default handling of a #endif line.
474        """
475        self.restore()
476
477    def do_define(self, t):
478        """
479        Default handling of a #define line.
480        """
481        _, name, args, expansion = t
482        try:
483            expansion = int(expansion)
484        except (TypeError, ValueError):
485            pass
486        if args:
487            evaluator = FunctionEvaluator(name, args[1:-1], expansion)
488            self.cpp_namespace[name] = evaluator
489        else:
490            self.cpp_namespace[name] = expansion
491
492    def do_undef(self, t):
493        """
494        Default handling of a #undef line.
495        """
496        try: del self.cpp_namespace[t[1]]
497        except KeyError: pass
498
499    def do_import(self, t):
500        """
501        Default handling of a #import line.
502        """
503        # XXX finish this -- maybe borrow/share logic from do_include()...?
504        pass
505
506    def do_include(self, t):
507        """
508        Default handling of a #include line.
509        """
510        t = self.resolve_include(t)
511        include_file = self.find_include_file(t)
512        if include_file:
513            #print "include_file =", include_file
514            self.result.append(include_file)
515            contents = self.read_file(include_file)
516            new_tuples = [('scons_current_file', include_file)] + \
517                         self.tupleize(contents) + \
518                         [('scons_current_file', self.current_file)]
519            self.tuples[:] = new_tuples + self.tuples
520
521    # Date: Tue, 22 Nov 2005 20:26:09 -0500
522    # From: Stefan Seefeld <seefeld@sympatico.ca>
523    #
524    # By the way, #include_next is not the same as #include. The difference
525    # being that #include_next starts its search in the path following the
526    # path that let to the including file. In other words, if your system
527    # include paths are ['/foo', '/bar'], and you are looking at a header
528    # '/foo/baz.h', it might issue an '#include_next <baz.h>' which would
529    # correctly resolve to '/bar/baz.h' (if that exists), but *not* see
530    # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html
531    # for more reasoning.
532    #
533    # I have no idea in what context 'import' might be used.
534
535    # XXX is #include_next really the same as #include ?
536    do_include_next = do_include
537
538    # Utility methods for handling resolution of include files.
539
540    def resolve_include(self, t):
541        """Resolve a tuple-ized #include line.
542
543        This handles recursive expansion of values without "" or <>
544        surrounding the name until an initial " or < is found, to handle
545                #include FILE
546        where FILE is a #define somewhere else.
547        """
548        s = t[1]
549        while not s[0] in '<"':
550            #print "s =", s
551            try:
552                s = self.cpp_namespace[s]
553            except KeyError:
554                m = function_name.search(s)
555                s = self.cpp_namespace[m.group(1)]
556                if callable(s):
557                    args = function_arg_separator.split(m.group(2))
558                    s = s(*args)
559            if not s:
560                return None
561        return (t[0], s[0], s[1:-1])
562
563    def all_include(self, t):
564        """
565        """
566        self.result.append(self.resolve_include(t))
567
568class DumbPreProcessor(PreProcessor):
569    """A preprocessor that ignores all #if/#elif/#else/#endif directives
570    and just reports back *all* of the #include files (like the classic
571    SCons scanner did).
572
573    This is functionally equivalent to using a regular expression to
574    find all of the #include lines, only slower.  It exists mainly as
575    an example of how the main PreProcessor class can be sub-classed
576    to tailor its behavior.
577    """
578    def __init__(self, *args, **kw):
579        PreProcessor.__init__(self, *args, **kw)
580        d = self.default_table
581        for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
582            d[func] = d[func] = self.do_nothing
583
584del __revision__
585
586# Local Variables:
587# tab-width:4
588# indent-tabs-mode:nil
589# End:
590# vim: set expandtab tabstop=4 shiftwidth=4:
591