1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 __revision__ = "src/engine/SCons/cpp.py 5023 2010/06/14 22:05:46 scons"
25
26 __doc__ = """
27 SCons C Pre-Processor module
28 """
29
30 import SCons.compat
31
32 import os
33 import re
34
35
36
37
38
39
40
41
42
43
44
45
46 cpp_lines_dict = {
47
48
49 ('if', 'elif', 'ifdef', 'ifndef',)
50 : '\s+(.+)',
51
52
53
54 ('import', 'include', 'include_next',)
55 : '\s*(.+)',
56
57
58 ('else', 'endif',) : '',
59
60
61
62
63
64
65 ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
66
67
68 ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)',
69 }
70
71
72
73
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
84
85
86
87 override = {
88 'if' : 'if(?!def)',
89 }
90 l = [override.get(x, x) for x in Table.keys()]
91
92
93
94
95
96
97
98 e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
99
100
101 CPP_Expression = re.compile(e, re.M)
102
103
104
105
106
107
108
109
110
111
112
113
114
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
128
129
130
131
132
133 l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True)
134
135
136
137 expr = '|'.join(map(re.escape, l))
138
139
140 CPP_to_Python_Ops_Expression = re.compile(expr)
141
142
143
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
154
155 for l in CPP_to_Python_Eval_List:
156 l[0] = re.compile(l[0])
157
158
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
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
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
202
203
204
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
221 line_continuations = re.compile('\\\\\r?\n')
222
223
224
225
226 function_name = re.compile('(\S+)\(([^)]*)\)')
227
228
229
230 function_arg_separator = re.compile(',\s*')
231
232
233
235 """
236 The main workhorse class for handling C pre-processing.
237 """
239 global Table
240
241 cpppath = tuple(cpppath)
242
243 self.searchpath = {
244 '"' : (current,) + cpppath,
245 '<' : cpppath + (current,),
246 }
247
248
249
250
251
252
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
260
261
262
263
264
265
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
274
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
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
314
315
316 self.dispatch_table[t[0]](t)
317 return self.finalize_result(fname)
318
319
320
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
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
338
340 """
341 Null method for when we explicitly want the action for a
342 specific preprocessor directive to do nothing.
343 """
344 pass
345
347 self.current_file = t[1]
348
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
363
366
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
383
384
385
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
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
417
418
419
435
437 """
438 Default handling of a #ifdef line.
439 """
440 self._do_if_else_condition(t[1] in self.cpp_namespace)
441
443 """
444 Default handling of a #ifndef line.
445 """
446 self._do_if_else_condition(t[1] not in self.cpp_namespace)
447
453
463
465 """
466 Default handling of a #else line.
467 """
468 pass
469
471 """
472 Default handling of a #endif line.
473 """
474 self.restore()
475
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
492 """
493 Default handling of a #undef line.
494 """
495 try: del self.cpp_namespace[t[1]]
496 except KeyError: pass
497
499 """
500 Default handling of a #import line.
501 """
502
503 pass
504
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
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
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535 do_include_next = do_include
536
537
538
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
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
566
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 """
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
586
587
588
589
590