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 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog"
25
26 __doc__ = """
27 SCons C Pre-Processor module
28 """
29 import SCons.compat
30
31 import os
32 import re
33
34
35
36
37
38
39
40
41
42
43
44
45 cpp_lines_dict = {
46
47
48 ('if', 'elif', 'ifdef', 'ifndef',)
49 : '\s+(.+)',
50
51
52
53 ('import', 'include', 'include_next',)
54 : '\s*(.+)',
55
56
57 ('else', 'endif',) : '',
58
59
60
61
62
63
64 ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
65
66
67 ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)',
68 }
69
70
71
72
73 Table = {}
74 for op_list, expr in cpp_lines_dict.items():
75 e = re.compile(expr)
76 for op in op_list:
77 Table[op] = e
78 del e
79 del op
80 del op_list
81
82
83
84
85
86 override = {
87 'if' : 'if(?!def)',
88 }
89 l = [override.get(x, x) for x in list(Table.keys())]
90
91
92
93
94
95
96
97 e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
98
99
100 CPP_Expression = re.compile(e, re.M)
101
102
103
104
105
106
107
108
109
110
111
112
113
114 CPP_to_Python_Ops_Dict = {
115 '!' : ' not ',
116 '!=' : ' != ',
117 '&&' : ' and ',
118 '||' : ' or ',
119 '?' : ' and ',
120 ':' : ' or ',
121 '\r' : '',
122 }
123
124 CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)]
125
126
127
128
129
130
131
132 l = sorted(list(CPP_to_Python_Ops_Dict.keys()), key=lambda a: len(a), reverse=True)
133
134
135
136 expr = '|'.join(map(re.escape, l))
137
138
139 CPP_to_Python_Ops_Expression = re.compile(expr)
140
141
142
143 CPP_to_Python_Eval_List = [
144 ['defined\s+(\w+)', '"\\1" in __dict__'],
145 ['defined\s*\((\w+)\)', '"\\1" in __dict__'],
146 ['/\*.*\*/', ''],
147 ['/\*.*', ''],
148 ['//.*', ''],
149 ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'],
150 ]
151
152
153
154 for l in CPP_to_Python_Eval_List:
155 l[0] = re.compile(l[0])
156
157
159 """
160 Converts a C pre-processor expression into an equivalent
161 Python expression that can be evaluated.
162 """
163 s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s)
164 for expr, repl in CPP_to_Python_Eval_List:
165 s = expr.sub(repl, s)
166 return s
167
168
169
170 del expr
171 del l
172 del override
173
174
175
177 """
178 Handles delayed evaluation of a #define function call.
179 """
180 - def __init__(self, name, args, expansion):
181 """
182 Squirrels away the arguments and expansion value of a #define
183 macro function for later evaluation when we must actually expand
184 a value that uses it.
185 """
186 self.name = name
187 self.args = function_arg_separator.split(args)
188 try:
189 expansion = expansion.split('##')
190 except AttributeError:
191 pass
192 self.expansion = expansion
194 """
195 Evaluates the expansion of a #define macro function called
196 with the specified values.
197 """
198 if len(self.args) != len(values):
199 raise ValueError("Incorrect number of arguments to `%s'" % self.name)
200
201
202
203
204 locals = {}
205 for k, v in zip(self.args, values):
206 locals[k] = v
207
208 parts = []
209 for s in self.expansion:
210 if not s in self.args:
211 s = repr(s)
212 parts.append(s)
213 statement = ' + '.join(parts)
214
215 return eval(statement, globals(), locals)
216
217
218
219
220 line_continuations = re.compile('\\\\\r?\n')
221
222
223
224
225 function_name = re.compile('(\S+)\(([^)]*)\)')
226
227
228
229 function_arg_separator = re.compile(',\s*')
230
231
232
234 """
235 The main workhorse class for handling C pre-processing.
236 """
237 - def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
238 global Table
239
240 cpppath = tuple(cpppath)
241
242 self.searchpath = {
243 '"' : (current,) + cpppath,
244 '<' : cpppath + (current,),
245 }
246
247
248
249
250
251
252 self.cpp_namespace = dict.copy()
253 self.cpp_namespace['__dict__'] = self.cpp_namespace
254
255 if all:
256 self.do_include = self.all_include
257
258
259
260
261
262
263
264
265 d = {
266 'scons_current_file' : self.scons_current_file
267 }
268 for op in list(Table.keys()):
269 d[op] = getattr(self, 'do_' + op)
270 self.default_table = d
271
272
273
275 """
276 Turns the contents of a file into a list of easily-processed
277 tuples describing the CPP lines in the file.
278
279 The first element of each tuple is the line's preprocessor
280 directive (#if, #include, #define, etc., minus the initial '#').
281 The remaining elements are specific to the type of directive, as
282 pulled apart by the regular expression.
283 """
284 global CPP_Expression, Table
285 contents = line_continuations.sub('', contents)
286 cpp_tuples = CPP_Expression.findall(contents)
287 return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples]
288
290 """
291 Pre-processes a file.
292
293 This is the main public entry point.
294 """
295 self.current_file = file
296 return self.process_contents(self.read_file(file), file)
297
298 - def process_contents(self, contents, fname=None):
299 """
300 Pre-processes a file contents.
301
302 This is the main internal entry point.
303 """
304 self.stack = []
305 self.dispatch_table = self.default_table.copy()
306 self.current_file = fname
307 self.tuples = self.tupleize(contents)
308
309 self.initialize_result(fname)
310 while self.tuples:
311 t = self.tuples.pop(0)
312
313
314
315 self.dispatch_table[t[0]](t)
316 return self.finalize_result(fname)
317
318
319
321 """
322 Pushes the current dispatch table on the stack and re-initializes
323 the current dispatch table to the default.
324 """
325 self.stack.append(self.dispatch_table)
326 self.dispatch_table = self.default_table.copy()
327
329 """
330 Pops the previous dispatch table off the stack and makes it the
331 current one.
332 """
333 try: self.dispatch_table = self.stack.pop()
334 except IndexError: pass
335
336
337
339 """
340 Null method for when we explicitly want the action for a
341 specific preprocessor directive to do nothing.
342 """
343 pass
344
346 self.current_file = t[1]
347
349 """
350 Evaluates a C preprocessor expression.
351
352 This is done by converting it to a Python equivalent and
353 eval()ing it in the C preprocessor namespace we use to
354 track #define values.
355 """
356 t = CPP_to_Python(' '.join(t[1:]))
357 try: return eval(t, self.cpp_namespace)
358 except (NameError, TypeError): return 0
359
362
365
367 """
368 Finds the #include file for a given preprocessor tuple.
369 """
370 fname = t[2]
371 for d in self.searchpath[t[1]]:
372 if d == os.curdir:
373 f = fname
374 else:
375 f = os.path.join(d, fname)
376 if os.path.isfile(f):
377 return f
378 return None
379
381 with open(file) as f:
382 return f.read()
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 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
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
418
419
420
436
438 """
439 Default handling of a #ifdef line.
440 """
441 self._do_if_else_condition(t[1] in self.cpp_namespace)
442
444 """
445 Default handling of a #ifndef line.
446 """
447 self._do_if_else_condition(t[1] not in self.cpp_namespace)
448
454
464
466 """
467 Default handling of a #else line.
468 """
469 pass
470
472 """
473 Default handling of a #endif line.
474 """
475 self.restore()
476
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
493 """
494 Default handling of a #undef line.
495 """
496 try: del self.cpp_namespace[t[1]]
497 except KeyError: pass
498
500 """
501 Default handling of a #import line.
502 """
503
504 pass
505
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
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
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536 do_include_next = do_include
537
538
539
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
546 #include FILE
547
548 where FILE is a #define somewhere else."""
549
550 s = t[1]
551 while not s[0] in '<"':
552
553 try:
554 s = self.cpp_namespace[s]
555 except KeyError:
556 m = function_name.search(s)
557 s = self.cpp_namespace[m.group(1)]
558 if callable(s):
559 args = function_arg_separator.split(m.group(2))
560 s = s(*args)
561 if not s:
562 return None
563 return (t[0], s[0], s[1:-1])
564
569
571 """A preprocessor that ignores all #if/#elif/#else/#endif directives
572 and just reports back *all* of the #include files (like the classic
573 SCons scanner did).
574
575 This is functionally equivalent to using a regular expression to
576 find all of the #include lines, only slower. It exists mainly as
577 an example of how the main PreProcessor class can be sub-classed
578 to tailor its behavior.
579 """
581 PreProcessor.__init__(self, *args, **kw)
582 d = self.default_table
583 for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
584 d[func] = d[func] = self.do_nothing
585
586 del __revision__
587
588
589
590
591
592
593