35
35
36
36
def preprocess (source , * ,
37
37
incldirs = None ,
38
+ includes = None ,
38
39
macros = None ,
39
40
samefiles = None ,
40
41
filename = None ,
42
+ cwd = None ,
41
43
tool = True ,
42
44
):
43
45
"""...
44
46
45
47
CWD should be the project root and "source" should be relative.
46
48
"""
47
49
if tool :
48
- logger .debug (f'CWD: { os .getcwd ()!r} ' )
49
- logger .debug (f'incldirs: { incldirs !r} ' )
50
- logger .debug (f'macros: { macros !r} ' )
50
+ if not cwd :
51
+ cwd = os .getcwd ()
52
+ logger .debug (f'CWD: { cwd !r} ' )
53
+ logger .debug (f'incldirs: { incldirs !r} ' )
54
+ logger .debug (f'includes: { includes !r} ' )
55
+ logger .debug (f'macros: { macros !r} ' )
51
56
logger .debug (f'samefiles: { samefiles !r} ' )
52
57
_preprocess = _get_preprocessor (tool )
53
58
with _good_file (source , filename ) as source :
54
- return _preprocess (source , incldirs , macros , samefiles ) or ()
59
+ return _preprocess (
60
+ source ,
61
+ incldirs ,
62
+ includes ,
63
+ macros ,
64
+ samefiles ,
65
+ cwd ,
66
+ ) or ()
55
67
else :
56
68
source , filename = _resolve_source (source , filename )
57
69
# We ignore "includes", "macros", etc.
58
- return _pure .preprocess (source , filename )
70
+ return _pure .preprocess (source , filename , cwd )
59
71
60
72
# if _run() returns just the lines:
61
73
# text = _run(source)
@@ -72,6 +84,7 @@ def preprocess(source, *,
72
84
73
85
def get_preprocessor (* ,
74
86
file_macros = None ,
87
+ file_includes = None ,
75
88
file_incldirs = None ,
76
89
file_same = None ,
77
90
ignore_exc = False ,
@@ -80,27 +93,39 @@ def get_preprocessor(*,
80
93
_preprocess = preprocess
81
94
if file_macros :
82
95
file_macros = tuple (_parse_macros (file_macros ))
96
+ if file_includes :
97
+ file_includes = tuple (_parse_includes (file_includes ))
83
98
if file_incldirs :
84
99
file_incldirs = tuple (_parse_incldirs (file_incldirs ))
85
100
if file_same :
86
- file_same = tuple (file_same )
101
+ file_same = dict (file_same or () )
87
102
if not callable (ignore_exc ):
88
103
ignore_exc = (lambda exc , _ig = ignore_exc : _ig )
89
104
90
105
def get_file_preprocessor (filename ):
91
106
filename = filename .strip ()
92
107
if file_macros :
93
108
macros = list (_resolve_file_values (filename , file_macros ))
109
+ if file_includes :
110
+ # There's a small chance we could need to filter out any
111
+ # includes that import "filename". It isn't clear that it's
112
+ # a problem any longer. If we do end up filtering then
113
+ # it may make sense to use c_common.fsutil.match_path_tail().
114
+ includes = [i for i , in _resolve_file_values (filename , file_includes )]
94
115
if file_incldirs :
95
116
incldirs = [v for v , in _resolve_file_values (filename , file_incldirs )]
117
+ if file_same :
118
+ samefiles = _resolve_samefiles (filename , file_same )
96
119
97
120
def preprocess (** kwargs ):
98
121
if file_macros and 'macros' not in kwargs :
99
122
kwargs ['macros' ] = macros
123
+ if file_includes and 'includes' not in kwargs :
124
+ kwargs ['includes' ] = includes
100
125
if file_incldirs and 'incldirs' not in kwargs :
101
- kwargs ['incldirs' ] = [ v for v , in _resolve_file_values ( filename , file_incldirs )]
102
- if file_same and 'file_same ' not in kwargs :
103
- kwargs ['samefiles' ] = file_same
126
+ kwargs ['incldirs' ] = incldirs
127
+ if file_same and 'samefiles ' not in kwargs :
128
+ kwargs ['samefiles' ] = samefiles
104
129
kwargs .setdefault ('filename' , filename )
105
130
with handling_errors (ignore_exc , log_err = log_err ):
106
131
return _preprocess (filename , ** kwargs )
@@ -120,6 +145,11 @@ def _parse_macros(macros):
120
145
yield row
121
146
122
147
148
+ def _parse_includes (includes ):
149
+ for row , srcfile in _parse_table (includes , '\t ' , 'glob\t include' , default = None ):
150
+ yield row
151
+
152
+
123
153
def _parse_incldirs (incldirs ):
124
154
for row , srcfile in _parse_table (incldirs , '\t ' , 'glob\t dirname' , default = None ):
125
155
glob , dirname = row
@@ -130,6 +160,43 @@ def _parse_incldirs(incldirs):
130
160
yield row
131
161
132
162
163
+ def _resolve_samefiles (filename , file_same ):
164
+ assert '*' not in filename , (filename ,)
165
+ assert os .path .normpath (filename ) == filename , (filename ,)
166
+ _ , suffix = os .path .splitext (filename )
167
+ samefiles = []
168
+ for patterns , in _resolve_file_values (filename , file_same .items ()):
169
+ for pattern in patterns :
170
+ same = _resolve_samefile (filename , pattern , suffix )
171
+ if not same :
172
+ continue
173
+ samefiles .append (same )
174
+ return samefiles
175
+
176
+
177
+ def _resolve_samefile (filename , pattern , suffix ):
178
+ if pattern == filename :
179
+ return None
180
+ if pattern .endswith (os .path .sep ):
181
+ pattern += f'*{ suffix } '
182
+ assert os .path .normpath (pattern ) == pattern , (pattern ,)
183
+ if '*' in os .path .dirname (pattern ):
184
+ raise NotImplementedError ((filename , pattern ))
185
+ if '*' not in os .path .basename (pattern ):
186
+ return pattern
187
+
188
+ common = os .path .commonpath ([filename , pattern ])
189
+ relpattern = pattern [len (common ) + len (os .path .sep ):]
190
+ relpatterndir = os .path .dirname (relpattern )
191
+ relfile = filename [len (common ) + len (os .path .sep ):]
192
+ if os .path .basename (pattern ) == '*' :
193
+ return os .path .join (common , relpatterndir , relfile )
194
+ elif os .path .basename (relpattern ) == '*' + suffix :
195
+ return os .path .join (common , relpatterndir , relfile )
196
+ else :
197
+ raise NotImplementedError ((filename , pattern ))
198
+
199
+
133
200
@contextlib .contextmanager
134
201
def handling_errors (ignore_exc = None , * , log_err = None ):
135
202
try :
0 commit comments