Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: scons-2.0.1/engine/SCons/cpp.py

Issue 6711079: Added an unmodified copy of SCons to third_party. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/third_party/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #
2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S Cons 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 5134 2010/08/16 23:02:40 bdeegan"
25
26 __doc__ = """
27 SCons C Pre-Processor module
28 """
29 #TODO 2.3 and before has no sorted()
30 import SCons.compat
31
32 import os
33 import 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.
46 cpp_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.
74 Table = {}
75 for op_list, expr in cpp_lines_dict.items():
76 e = re.compile(expr)
77 for op in op_list:
78 Table[op] = e
79 del e
80 del op
81 del 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."
87 override = {
88 'if' : 'if(?!def)',
89 }
90 l = [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.
98 e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
99
100 # And last but not least, compile the expression.
101 CPP_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.
115 CPP_to_Python_Ops_Dict = {
116 '!' : ' not ',
117 '!=' : ' != ',
118 '&&' : ' and ',
119 '||' : ' or ',
120 '?' : ' and ',
121 ':' : ' or ',
122 '\r' : '',
123 }
124
125 CPP_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?
133 l = 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.
137 expr = '|'.join(map(re.escape, l))
138
139 # ...and compile the expression.
140 CPP_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.
144 CPP_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.
155 for 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.
159 def 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
171 del expr
172 del l
173 del override
174
175
176
177 class 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.
221 line_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.
226 function_name = re.compile('(\S+)\(([^)]*)\)')
227
228 # Split a string containing comma-separated function call arguments into
229 # the separate arguments.
230 function_arg_separator = re.compile(',\s*')
231
232
233
234 class 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 d['import'] = self.do_import
399 d['include'] = self.do_include
400 d['include_next'] = self.do_include
401
402 def stop_handling_includes(self, t=None):
403 """
404 Causes the PreProcessor object to stop processing #import,
405 #include and #include_next lines.
406
407 This method will be called when a #if, #ifdef, #ifndef or #elif
408 evaluates False, or when we reach the #else in a #if, #ifdef,
409 #ifndef or #elif block where a condition already evaluated True.
410 """
411 d = self.dispatch_table
412 d['import'] = self.do_nothing
413 d['include'] = self.do_nothing
414 d['include_next'] = self.do_nothing
415
416 # Default methods for handling all of the preprocessor directives.
417 # (Note that what actually gets called for a given directive at any
418 # point in time is really controlled by the dispatch_table.)
419
420 def _do_if_else_condition(self, condition):
421 """
422 Common logic for evaluating the conditions on #if, #ifdef and
423 #ifndef lines.
424 """
425 self.save()
426 d = self.dispatch_table
427 if condition:
428 self.start_handling_includes()
429 d['elif'] = self.stop_handling_includes
430 d['else'] = self.stop_handling_includes
431 else:
432 self.stop_handling_includes()
433 d['elif'] = self.do_elif
434 d['else'] = self.start_handling_includes
435
436 def do_ifdef(self, t):
437 """
438 Default handling of a #ifdef line.
439 """
440 self._do_if_else_condition(t[1] in self.cpp_namespace)
441
442 def do_ifndef(self, t):
443 """
444 Default handling of a #ifndef line.
445 """
446 self._do_if_else_condition(t[1] not in self.cpp_namespace)
447
448 def do_if(self, t):
449 """
450 Default handling of a #if line.
451 """
452 self._do_if_else_condition(self.eval_expression(t))
453
454 def do_elif(self, t):
455 """
456 Default handling of a #elif line.
457 """
458 d = self.dispatch_table
459 if self.eval_expression(t):
460 self.start_handling_includes()
461 d['elif'] = self.stop_handling_includes
462 d['else'] = self.stop_handling_includes
463
464 def do_else(self, t):
465 """
466 Default handling of a #else line.
467 """
468 pass
469
470 def do_endif(self, t):
471 """
472 Default handling of a #endif line.
473 """
474 self.restore()
475
476 def do_define(self, t):
477 """
478 Default handling of a #define line.
479 """
480 _, name, args, expansion = t
481 try:
482 expansion = int(expansion)
483 except (TypeError, ValueError):
484 pass
485 if args:
486 evaluator = FunctionEvaluator(name, args[1:-1], expansion)
487 self.cpp_namespace[name] = evaluator
488 else:
489 self.cpp_namespace[name] = expansion
490
491 def do_undef(self, t):
492 """
493 Default handling of a #undef line.
494 """
495 try: del self.cpp_namespace[t[1]]
496 except KeyError: pass
497
498 def do_import(self, t):
499 """
500 Default handling of a #import line.
501 """
502 # XXX finish this -- maybe borrow/share logic from do_include()...?
503 pass
504
505 def do_include(self, t):
506 """
507 Default handling of a #include line.
508 """
509 t = self.resolve_include(t)
510 include_file = self.find_include_file(t)
511 if include_file:
512 #print "include_file =", include_file
513 self.result.append(include_file)
514 contents = self.read_file(include_file)
515 new_tuples = [('scons_current_file', include_file)] + \
516 self.tupleize(contents) + \
517 [('scons_current_file', self.current_file)]
518 self.tuples[:] = new_tuples + self.tuples
519
520 # Date: Tue, 22 Nov 2005 20:26:09 -0500
521 # From: Stefan Seefeld <seefeld@sympatico.ca>
522 #
523 # By the way, #include_next is not the same as #include. The difference
524 # being that #include_next starts its search in the path following the
525 # path that let to the including file. In other words, if your system
526 # include paths are ['/foo', '/bar'], and you are looking at a header
527 # '/foo/baz.h', it might issue an '#include_next <baz.h>' which would
528 # correctly resolve to '/bar/baz.h' (if that exists), but *not* see
529 # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html
530 # for more reasoning.
531 #
532 # I have no idea in what context 'import' might be used.
533
534 # XXX is #include_next really the same as #include ?
535 do_include_next = do_include
536
537 # Utility methods for handling resolution of include files.
538
539 def resolve_include(self, t):
540 """Resolve a tuple-ized #include line.
541
542 This handles recursive expansion of values without "" or <>
543 surrounding the name until an initial " or < is found, to handle
544 #include FILE
545 where FILE is a #define somewhere else.
546 """
547 s = t[1]
548 while not s[0] in '<"':
549 #print "s =", s
550 try:
551 s = self.cpp_namespace[s]
552 except KeyError:
553 m = function_name.search(s)
554 s = self.cpp_namespace[m.group(1)]
555 if callable(s):
556 args = function_arg_separator.split(m.group(2))
557 s = s(*args)
558 if not s:
559 return None
560 return (t[0], s[0], s[1:-1])
561
562 def all_include(self, t):
563 """
564 """
565 self.result.append(self.resolve_include(t))
566
567 class DumbPreProcessor(PreProcessor):
568 """A preprocessor that ignores all #if/#elif/#else/#endif directives
569 and just reports back *all* of the #include files (like the classic
570 SCons scanner did).
571
572 This is functionally equivalent to using a regular expression to
573 find all of the #include lines, only slower. It exists mainly as
574 an example of how the main PreProcessor class can be sub-classed
575 to tailor its behavior.
576 """
577 def __init__(self, *args, **kw):
578 PreProcessor.__init__(self, *args, **kw)
579 d = self.default_table
580 for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
581 d[func] = d[func] = self.do_nothing
582
583 del __revision__
584
585 # Local Variables:
586 # tab-width:4
587 # indent-tabs-mode:nil
588 # End:
589 # vim: set expandtab tabstop=4 shiftwidth=4:
OLDNEW
« no previous file with comments | « scons-2.0.1/engine/SCons/compat/_scons_subprocess.py ('k') | scons-2.0.1/engine/SCons/dblite.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698