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 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan"
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') : '\s*(.+)',
49
50
51
52 ('ifdef', 'ifndef',): '\s+(.+)',
53
54
55
56 ('import', 'include', 'include_next',)
57 : '\s*(.+)',
58
59
60 ('else', 'endif',) : '',
61
62
63
64
65
66
67 ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)',
68
69
70 ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)',
71 }
72
73
74
75
76 Table = {}
77 for op_list, expr in cpp_lines_dict.items():
78 e = re.compile(expr)
79 for op in op_list:
80 Table[op] = e
81 del e
82 del op
83 del op_list
84
85
86
87
88
89 override = {
90 'if' : 'if(?!n?def)',
91 }
92 l = [override.get(x, x) for x in list(Table.keys())]
93
94
95
96
97
98
99
100 e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$'
101
102
103 CPP_Expression = re.compile(e, re.M)
104
105
106
107
108
109
110
111
112
113
114
115
116
117 CPP_to_Python_Ops_Dict = {
118 '!' : ' not ',
119 '!=' : ' != ',
120 '&&' : ' and ',
121 '||' : ' or ',
122 '?' : ' and ',
123 ':' : ' or ',
124 '\r' : '',
125 }
126
127 CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)]
128
129
130
131
132
133
134
135 l = sorted(list(CPP_to_Python_Ops_Dict.keys()), key=lambda a: len(a), reverse=True)
136
137
138
139 expr = '|'.join(map(re.escape, l))
140
141
142 CPP_to_Python_Ops_Expression = re.compile(expr)
143
144
145
146 CPP_to_Python_Eval_List = [
147 ['defined\s+(\w+)', '"\\1" in __dict__'],
148 ['defined\s*\((\w+)\)', '"\\1" in __dict__'],
149 ['/\*.*\*/', ''],
150 ['/\*.*', ''],
151 ['//.*', ''],
152 ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'],
153 ]
154
155
156
157 for l in CPP_to_Python_Eval_List:
158 l[0] = re.compile(l[0])
159
160
170
171
172
173 del expr
174 del l
175 del override
176
177
178
180 """
181 Handles delayed evaluation of a #define function call.
182 """
183 - def __init__(self, name, args, expansion):
184 """
185 Squirrels away the arguments and expansion value of a #define
186 macro function for later evaluation when we must actually expand
187 a value that uses it.
188 """
189 self.name = name
190 self.args = function_arg_separator.split(args)
191 try:
192 expansion = expansion.split('##')
193 except AttributeError:
194 pass
195 self.expansion = expansion
197 """
198 Evaluates the expansion of a #define macro function called
199 with the specified values.
200 """
201 if len(self.args) != len(values):
202 raise ValueError("Incorrect number of arguments to `%s'" % self.name)
203
204
205
206
207 locals = {}
208 for k, v in zip(self.args, values):
209 locals[k] = v
210
211 parts = []
212 for s in self.expansion:
213 if not s in self.args:
214 s = repr(s)
215 parts.append(s)
216 statement = ' + '.join(parts)
217
218 return eval(statement, globals(), locals)
219
220
221
222
223 line_continuations = re.compile('\\\\\r?\n')
224
225
226
227
228 function_name = re.compile('(\S+)\(([^)]*)\)')
229
230
231
232 function_arg_separator = re.compile(',\s*')
233
234
235
237 """
238 The main workhorse class for handling C pre-processing.
239 """
240 - def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
241 global Table
242
243 cpppath = tuple(cpppath)
244
245 self.searchpath = {
246 '"' : (current,) + cpppath,
247 '<' : cpppath + (current,),
248 }
249
250
251
252
253
254
255 self.cpp_namespace = dict.copy()
256 self.cpp_namespace['__dict__'] = self.cpp_namespace
257
258 if all:
259 self.do_include = self.all_include
260
261
262
263
264
265
266
267
268 d = {
269 'scons_current_file' : self.scons_current_file
270 }
271 for op in list(Table.keys()):
272 d[op] = getattr(self, 'do_' + op)
273 self.default_table = d
274
275
276
278 """
279 Turns the contents of a file into a list of easily-processed
280 tuples describing the CPP lines in the file.
281
282 The first element of each tuple is the line's preprocessor
283 directive (#if, #include, #define, etc., minus the initial '#').
284 The remaining elements are specific to the type of directive, as
285 pulled apart by the regular expression.
286 """
287 global CPP_Expression, Table
288 contents = line_continuations.sub('', contents)
289 cpp_tuples = CPP_Expression.findall(contents)
290 return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples]
291
300
301 - def process_contents(self, contents, fname=None):
302 """
303 Pre-processes a file contents.
304
305 This is the main internal entry point.
306 """
307 self.stack = []
308 self.dispatch_table = self.default_table.copy()
309 self.current_file = fname
310 self.tuples = self.tupleize(contents)
311
312 self.initialize_result(fname)
313 while self.tuples:
314 t = self.tuples.pop(0)
315
316
317
318 self.dispatch_table[t[0]](t)
319 return self.finalize_result(fname)
320
321
322
324 """
325 Pushes the current dispatch table on the stack and re-initializes
326 the current dispatch table to the default.
327 """
328 self.stack.append(self.dispatch_table)
329 self.dispatch_table = self.default_table.copy()
330
332 """
333 Pops the previous dispatch table off the stack and makes it the
334 current one.
335 """
336 try: self.dispatch_table = self.stack.pop()
337 except IndexError: pass
338
339
340
342 """
343 Null method for when we explicitly want the action for a
344 specific preprocessor directive to do nothing.
345 """
346 pass
347
349 self.current_file = t[1]
350
352 """
353 Evaluates a C preprocessor expression.
354
355 This is done by converting it to a Python equivalent and
356 eval()ing it in the C preprocessor namespace we use to
357 track #define values.
358 """
359 t = CPP_to_Python(' '.join(t[1:]))
360 try: return eval(t, self.cpp_namespace)
361 except (NameError, TypeError): return 0
362
365
368
370 """
371 Finds the #include file for a given preprocessor tuple.
372 """
373 fname = t[2]
374 for d in self.searchpath[t[1]]:
375 if d == os.curdir:
376 f = fname
377 else:
378 f = os.path.join(d, fname)
379 if os.path.isfile(f):
380 return f
381 return None
382
386
387
388
390 """
391 Causes the PreProcessor object to start processing #import,
392 #include and #include_next lines.
393
394 This method will be called when a #if, #ifdef, #ifndef or #elif
395 evaluates True, or when we reach the #else in a #if, #ifdef,
396 #ifndef or #elif block where a condition already evaluated
397 False.
398
399 """
400 d = self.dispatch_table
401 p = self.stack[-1] if self.stack else self.default_table
402
403 for k in ('import', 'include', 'include_next'):
404 d[k] = p[k]
405
407 """
408 Causes the PreProcessor object to stop processing #import,
409 #include and #include_next lines.
410
411 This method will be called when a #if, #ifdef, #ifndef or #elif
412 evaluates False, or when we reach the #else in a #if, #ifdef,
413 #ifndef or #elif block where a condition already evaluated True.
414 """
415 d = self.dispatch_table
416 d['import'] = self.do_nothing
417 d['include'] = self.do_nothing
418 d['include_next'] = self.do_nothing
419
420
421
422
423
439
441 """
442 Default handling of a #ifdef line.
443 """
444 self._do_if_else_condition(t[1] in self.cpp_namespace)
445
447 """
448 Default handling of a #ifndef line.
449 """
450 self._do_if_else_condition(t[1] not in self.cpp_namespace)
451
457
467
469 """
470 Default handling of a #else line.
471 """
472 pass
473
475 """
476 Default handling of a #endif line.
477 """
478 self.restore()
479
481 """
482 Default handling of a #define line.
483 """
484 _, name, args, expansion = t
485 try:
486 expansion = int(expansion)
487 except (TypeError, ValueError):
488 pass
489 if args:
490 evaluator = FunctionEvaluator(name, args[1:-1], expansion)
491 self.cpp_namespace[name] = evaluator
492 else:
493 self.cpp_namespace[name] = expansion
494
496 """
497 Default handling of a #undef line.
498 """
499 try: del self.cpp_namespace[t[1]]
500 except KeyError: pass
501
503 """
504 Default handling of a #import line.
505 """
506
507 pass
508
510 """
511 Default handling of a #include line.
512 """
513 t = self.resolve_include(t)
514 include_file = self.find_include_file(t)
515 if include_file:
516
517 self.result.append(include_file)
518 contents = self.read_file(include_file)
519 new_tuples = [('scons_current_file', include_file)] + \
520 self.tupleize(contents) + \
521 [('scons_current_file', self.current_file)]
522 self.tuples[:] = new_tuples + self.tuples
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539 do_include_next = do_include
540
541
542
544 """Resolve a tuple-ized #include line.
545
546 This handles recursive expansion of values without "" or <>
547 surrounding the name until an initial " or < is found, to handle
548
549 #include FILE
550
551 where FILE is a #define somewhere else."""
552
553 s = t[1]
554 while not s[0] in '<"':
555
556 try:
557 s = self.cpp_namespace[s]
558 except KeyError:
559 m = function_name.search(s)
560 s = self.cpp_namespace[m.group(1)]
561 if callable(s):
562 args = function_arg_separator.split(m.group(2))
563 s = s(*args)
564 if not s:
565 return None
566 return (t[0], s[0], s[1:-1])
567
572
574 """A preprocessor that ignores all #if/#elif/#else/#endif directives
575 and just reports back *all* of the #include files (like the classic
576 SCons scanner did).
577
578 This is functionally equivalent to using a regular expression to
579 find all of the #include lines, only slower. It exists mainly as
580 an example of how the main PreProcessor class can be sub-classed
581 to tailor its behavior.
582 """
584 PreProcessor.__init__(self, *args, **kw)
585 d = self.default_table
586 for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']:
587 d[func] = d[func] = self.do_nothing
588
589 del __revision__
590
591
592
593
594
595
596