1 | # |
---|
2 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
3 | # Copyright 2010-2014 Chris Johns (chrisj@rtems.org) |
---|
4 | # All rights reserved. |
---|
5 | # |
---|
6 | # This file is part of the RTEMS Tools package in 'rtems-tools'. |
---|
7 | # |
---|
8 | # Redistribution and use in source and binary forms, with or without |
---|
9 | # modification, are permitted provided that the following conditions are met: |
---|
10 | # |
---|
11 | # 1. Redistributions of source code must retain the above copyright notice, |
---|
12 | # this list of conditions and the following disclaimer. |
---|
13 | # |
---|
14 | # 2. Redistributions in binary form must reproduce the above copyright notice, |
---|
15 | # this list of conditions and the following disclaimer in the documentation |
---|
16 | # and/or other materials provided with the distribution. |
---|
17 | # |
---|
18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
21 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
---|
22 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
23 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
24 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
26 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
27 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
28 | # POSSIBILITY OF SUCH DAMAGE. |
---|
29 | # |
---|
30 | |
---|
31 | # |
---|
32 | # This code is based on a tool I wrote to parse RPM spec files in the RTEMS |
---|
33 | # project. This is now a configuration file format that has moved away from the |
---|
34 | # spec file format to support the specific needs of cross-compiling GCC. This |
---|
35 | # module parses a configuration file into Python data types that can be used by |
---|
36 | # other software modules. |
---|
37 | # |
---|
38 | |
---|
39 | import copy |
---|
40 | import os |
---|
41 | import re |
---|
42 | import sys |
---|
43 | |
---|
44 | try: |
---|
45 | import error |
---|
46 | import execute |
---|
47 | import log |
---|
48 | import options |
---|
49 | import path |
---|
50 | except KeyboardInterrupt: |
---|
51 | print 'user terminated' |
---|
52 | sys.exit(1) |
---|
53 | except: |
---|
54 | print 'error: unknown application load error' |
---|
55 | sys.exit(1) |
---|
56 | |
---|
57 | def _check_bool(value): |
---|
58 | if value.isdigit(): |
---|
59 | if int(value) == 0: |
---|
60 | istrue = False |
---|
61 | else: |
---|
62 | istrue = True |
---|
63 | else: |
---|
64 | istrue = None |
---|
65 | return istrue |
---|
66 | |
---|
67 | class file(object): |
---|
68 | """Parse a config file.""" |
---|
69 | |
---|
70 | def __init__(self, name, opts, macros = None, directives = None, ignores = None): |
---|
71 | self.opts = opts |
---|
72 | if macros is None: |
---|
73 | self.macros = opts.defaults |
---|
74 | else: |
---|
75 | self.macros = macros |
---|
76 | self.init_name = name |
---|
77 | self.directives = ['%include'] |
---|
78 | if directives: |
---|
79 | self.directives += directives |
---|
80 | self.ignores = ignores |
---|
81 | log.trace('config: %s' % (name)) |
---|
82 | self.disable_macro_reassign = False |
---|
83 | self.configpath = [] |
---|
84 | self.wss = re.compile(r'\s+') |
---|
85 | self.tags = re.compile(r':+') |
---|
86 | self.sf = re.compile(r'%\([^\)]+\)') |
---|
87 | for arg in self.opts.args: |
---|
88 | if arg.startswith('--with-') or arg.startswith('--without-'): |
---|
89 | label = arg[2:].lower().replace('-', '_') |
---|
90 | self.macros.define(label) |
---|
91 | self._includes = [] |
---|
92 | self.load_depth = 0 |
---|
93 | |
---|
94 | def __del__(self): |
---|
95 | pass |
---|
96 | |
---|
97 | def __str__(self): |
---|
98 | |
---|
99 | def _dict(dd): |
---|
100 | s = '' |
---|
101 | ddl = dd.keys() |
---|
102 | ddl.sort() |
---|
103 | for d in ddl: |
---|
104 | s += ' ' + d + ': ' + dd[d] + '\n' |
---|
105 | return s |
---|
106 | |
---|
107 | s = 'config: %s' % ('.'.join(self.configpath)) + \ |
---|
108 | '\n' + str(self.opts) + \ |
---|
109 | '\nlines parsed: %d' % (self.lc) + \ |
---|
110 | '\nname: ' + self.name + \ |
---|
111 | '\nmacros:\n' + str(self.macros) |
---|
112 | return s |
---|
113 | |
---|
114 | def _name_line_msg(self, msg): |
---|
115 | return '%s:%d: %s' % (path.basename(self.init_name), self.lc, msg) |
---|
116 | |
---|
117 | def _output(self, text): |
---|
118 | if not self.opts.quiet(): |
---|
119 | log.output(text) |
---|
120 | |
---|
121 | def _error(self, msg): |
---|
122 | err = 'error: %s' % (self._name_line_msg(msg)) |
---|
123 | log.stderr(err) |
---|
124 | log.output(err) |
---|
125 | self.in_error = True |
---|
126 | if not self.opts.dry_run(): |
---|
127 | log.stderr('warning: switched to dry run due to errors') |
---|
128 | self.opts.set_dry_run() |
---|
129 | |
---|
130 | def _label(self, name): |
---|
131 | if name.startswith('%{') and name[-1] is '}': |
---|
132 | return name |
---|
133 | return '%{' + name.lower() + '}' |
---|
134 | |
---|
135 | def _macro_split(self, s): |
---|
136 | '''Split the string (s) up by macros. Only split on the |
---|
137 | outter level. Nested levels will need to split with futher calls.''' |
---|
138 | trace_me = False |
---|
139 | if trace_me: |
---|
140 | print '------------------------------------------------------' |
---|
141 | macros = [] |
---|
142 | nesting = [] |
---|
143 | has_braces = False |
---|
144 | c = 0 |
---|
145 | while c < len(s): |
---|
146 | if trace_me: |
---|
147 | print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting |
---|
148 | # |
---|
149 | # We need to watch for shell type variables or the form '${var}' because |
---|
150 | # they can upset the brace matching. |
---|
151 | # |
---|
152 | if s[c] == '%' or s[c] == '$': |
---|
153 | start = s[c] |
---|
154 | c += 1 |
---|
155 | if c == len(s): |
---|
156 | continue |
---|
157 | # |
---|
158 | # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ? |
---|
159 | # |
---|
160 | if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'): |
---|
161 | continue |
---|
162 | elif not s[c].isspace(): |
---|
163 | # |
---|
164 | # If this is a shell macro and we are at the outter |
---|
165 | # level or is '$var' forget it and move on. |
---|
166 | # |
---|
167 | if start == '$' and (s[c] != '{' or len(nesting) == 0): |
---|
168 | continue |
---|
169 | if s[c] == '{': |
---|
170 | this_has_braces = True |
---|
171 | else: |
---|
172 | this_has_braces = False |
---|
173 | nesting.append((c - 1, has_braces)) |
---|
174 | has_braces = this_has_braces |
---|
175 | elif len(nesting) > 0: |
---|
176 | if s[c] == '}' or (s[c].isspace() and not has_braces): |
---|
177 | # |
---|
178 | # Can have '%{?test: something %more}' where the |
---|
179 | # nested %more ends with the '}' which also ends |
---|
180 | # the outter macro. |
---|
181 | # |
---|
182 | if not has_braces: |
---|
183 | if s[c] == '}': |
---|
184 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
185 | nesting = nesting[:-1] |
---|
186 | if len(nesting) == 0: |
---|
187 | macros.append(s[macro_start:c].strip()) |
---|
188 | if len(nesting) > 0: |
---|
189 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
190 | nesting = nesting[:-1] |
---|
191 | if len(nesting) == 0: |
---|
192 | macros.append(s[macro_start:c + 1].strip()) |
---|
193 | c += 1 |
---|
194 | if trace_me: |
---|
195 | print 'ms:', macros |
---|
196 | if trace_me: |
---|
197 | print '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=' |
---|
198 | return macros |
---|
199 | |
---|
200 | def _shell(self, line): |
---|
201 | sl = self.sf.findall(line) |
---|
202 | if len(sl): |
---|
203 | e = execute.capture_execution() |
---|
204 | for s in sl: |
---|
205 | if options.host_windows: |
---|
206 | cmd = '%s -c "%s"' % (self.macros.expand('%{__sh}'), s[2:-1]) |
---|
207 | else: |
---|
208 | cmd = s[2:-1] |
---|
209 | exit_code, proc, output = e.shell(cmd) |
---|
210 | if exit_code == 0: |
---|
211 | line = line.replace(s, output) |
---|
212 | else: |
---|
213 | raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output)) |
---|
214 | return line |
---|
215 | |
---|
216 | def _expand(self, s): |
---|
217 | expand_count = 0 |
---|
218 | expanded = True |
---|
219 | while expanded: |
---|
220 | expand_count += 1 |
---|
221 | if expand_count > 500: |
---|
222 | raise error.general('macro expand looping: %s' % (s)) |
---|
223 | expanded = False |
---|
224 | ms = self._macro_split(s) |
---|
225 | for m in ms: |
---|
226 | mn = m |
---|
227 | # |
---|
228 | # A macro can be '%{macro}' or '%macro'. Turn the later into |
---|
229 | # the former. |
---|
230 | # |
---|
231 | show_warning = True |
---|
232 | if mn[1] != '{': |
---|
233 | if self.ignores is not None: |
---|
234 | for r in self.ignores: |
---|
235 | if r.match(mn) is not None: |
---|
236 | mn = None |
---|
237 | break |
---|
238 | else: |
---|
239 | mn = self._label(mn[1:]) |
---|
240 | show_warning = False |
---|
241 | else: |
---|
242 | mn = self._label(mn[1:]) |
---|
243 | show_warning = False |
---|
244 | elif m.startswith('%{expand'): |
---|
245 | colon = m.find(':') |
---|
246 | if colon < 8: |
---|
247 | log.warning('malformed expand macro, no colon found') |
---|
248 | else: |
---|
249 | e = self._expand(m[colon + 1:-1].strip()) |
---|
250 | s = s.replace(m, e) |
---|
251 | expanded = True |
---|
252 | mn = None |
---|
253 | elif m.startswith('%{with '): |
---|
254 | # |
---|
255 | # Change the ' ' to '_' because the macros have no spaces. |
---|
256 | # |
---|
257 | n = self._label('with_' + m[7:-1].strip()) |
---|
258 | if n in self.macros: |
---|
259 | s = s.replace(m, '1') |
---|
260 | else: |
---|
261 | s = s.replace(m, '0') |
---|
262 | expanded = True |
---|
263 | mn = None |
---|
264 | elif m.startswith('%{echo'): |
---|
265 | if not m.endswith('}'): |
---|
266 | log.warning("malformed conditional macro '%s'" % (m)) |
---|
267 | mn = None |
---|
268 | else: |
---|
269 | e = self._expand(m[6:-1].strip()) |
---|
270 | log.output('%s' % (self._name_line_msg(e))) |
---|
271 | s = '' |
---|
272 | expanded = True |
---|
273 | mn = None |
---|
274 | elif m.startswith('%{defined'): |
---|
275 | n = self._label(m[9:-1].strip()) |
---|
276 | if n in self.macros: |
---|
277 | s = s.replace(m, '1') |
---|
278 | else: |
---|
279 | s = s.replace(m, '0') |
---|
280 | expanded = True |
---|
281 | mn = None |
---|
282 | elif m.startswith('%{?') or m.startswith('%{!?'): |
---|
283 | if m[2] == '!': |
---|
284 | start = 4 |
---|
285 | else: |
---|
286 | start = 3 |
---|
287 | colon = m[start:].find(':') |
---|
288 | if colon < 0: |
---|
289 | if not m.endswith('}'): |
---|
290 | log.warning("malformed conditional macro '%s'" % (m)) |
---|
291 | mn = None |
---|
292 | else: |
---|
293 | mn = self._label(m[start:-1]) |
---|
294 | else: |
---|
295 | mn = self._label(m[start:start + colon]) |
---|
296 | if mn: |
---|
297 | if m.startswith('%{?'): |
---|
298 | istrue = False |
---|
299 | if mn in self.macros: |
---|
300 | # If defined and 0 then it is false. |
---|
301 | istrue = _check_bool(self.macros[mn]) |
---|
302 | if istrue is None: |
---|
303 | istrue = True |
---|
304 | if colon >= 0 and istrue: |
---|
305 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
306 | expanded = True |
---|
307 | mn = None |
---|
308 | elif not istrue: |
---|
309 | mn = '%{nil}' |
---|
310 | else: |
---|
311 | isfalse = True |
---|
312 | if mn in self.macros: |
---|
313 | istrue = _check_bool(self.macros[mn]) |
---|
314 | if istrue is None or istrue == True: |
---|
315 | isfalse = False |
---|
316 | if colon >= 0 and isfalse: |
---|
317 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
318 | expanded = True |
---|
319 | mn = None |
---|
320 | else: |
---|
321 | mn = '%{nil}' |
---|
322 | if mn: |
---|
323 | if mn.lower() in self.macros: |
---|
324 | s = s.replace(m, self.macros[mn.lower()]) |
---|
325 | expanded = True |
---|
326 | elif show_warning: |
---|
327 | self._error("macro '%s' not found" % (mn)) |
---|
328 | return self._shell(s) |
---|
329 | |
---|
330 | def _disable(self, config, ls): |
---|
331 | if len(ls) != 2: |
---|
332 | log.warning('invalid disable statement') |
---|
333 | else: |
---|
334 | if ls[1] == 'select': |
---|
335 | self.macros.lock_read_map() |
---|
336 | log.trace('config: %s: _disable_select: %s' % (self.init_name, ls[1])) |
---|
337 | else: |
---|
338 | log.warning('invalid disable statement: %s' % (ls[1])) |
---|
339 | |
---|
340 | def _select(self, config, ls): |
---|
341 | if len(ls) != 2: |
---|
342 | log.warning('invalid select statement') |
---|
343 | else: |
---|
344 | r = self.macros.set_read_map(ls[1]) |
---|
345 | log.trace('config: %s: _select: %s %s %r' % \ |
---|
346 | (self.init_name, r, ls[1], self.macros.maps())) |
---|
347 | |
---|
348 | def _define(self, config, ls): |
---|
349 | if len(ls) <= 1: |
---|
350 | log.warning('invalid macro definition') |
---|
351 | else: |
---|
352 | d = self._label(ls[1]) |
---|
353 | if self.disable_macro_reassign: |
---|
354 | if (d not in self.macros) or \ |
---|
355 | (d in self.macros and len(self.macros[d]) == 0): |
---|
356 | if len(ls) == 2: |
---|
357 | self.macros[d] = '1' |
---|
358 | else: |
---|
359 | self.macros[d] = ' '.join([f.strip() for f in ls[2:]]) |
---|
360 | else: |
---|
361 | log.warning("macro '%s' already defined" % (d)) |
---|
362 | else: |
---|
363 | if len(ls) == 2: |
---|
364 | self.macros[d] = '1' |
---|
365 | else: |
---|
366 | self.macros[d] = ' '.join([f.strip() for f in ls[2:]]) |
---|
367 | |
---|
368 | def _undefine(self, config, ls): |
---|
369 | if len(ls) <= 1: |
---|
370 | log.warning('invalid macro definition') |
---|
371 | else: |
---|
372 | mn = self._label(ls[1]) |
---|
373 | if mn in self.macros: |
---|
374 | del self.macros[mn] |
---|
375 | else: |
---|
376 | log.warning("macro '%s' not defined" % (mn)) |
---|
377 | |
---|
378 | def _ifs(self, config, ls, label, iftrue, isvalid, dir, info): |
---|
379 | in_iftrue = True |
---|
380 | data = [] |
---|
381 | while True: |
---|
382 | if isvalid and \ |
---|
383 | ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)): |
---|
384 | this_isvalid = True |
---|
385 | else: |
---|
386 | this_isvalid = False |
---|
387 | r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid) |
---|
388 | if r[0] == 'control': |
---|
389 | if r[1] == '%end': |
---|
390 | self._error(label + ' without %endif') |
---|
391 | raise error.general('terminating build') |
---|
392 | if r[1] == '%endif': |
---|
393 | log.trace('config: %s: _ifs: %s %s' % (self.init_name, r[1], this_isvalid)) |
---|
394 | return data |
---|
395 | if r[1] == '%else': |
---|
396 | in_iftrue = False |
---|
397 | elif r[0] == 'directive': |
---|
398 | if this_isvalid: |
---|
399 | if r[1] == '%include': |
---|
400 | self.load(r[2][0]) |
---|
401 | continue |
---|
402 | dir, info, data = self._process_directive(r, dir, info, data) |
---|
403 | elif r[0] == 'data': |
---|
404 | if this_isvalid: |
---|
405 | dir, info, data = self._process_data(r, dir, info, data) |
---|
406 | else: |
---|
407 | dir, info, data = self._process_block(r, dir, info, data) |
---|
408 | |
---|
409 | # @note is a directive extend missing |
---|
410 | |
---|
411 | def _if(self, config, ls, isvalid, dir, info, invert = False): |
---|
412 | |
---|
413 | def add(x, y): |
---|
414 | return x + ' ' + str(y) |
---|
415 | |
---|
416 | istrue = False |
---|
417 | if isvalid: |
---|
418 | if len(ls) == 2: |
---|
419 | s = ls[1] |
---|
420 | else: |
---|
421 | s = (ls[1] + ' ' + ls[2]) |
---|
422 | ifls = s.split() |
---|
423 | if len(ifls) == 1: |
---|
424 | # |
---|
425 | # Check if '%if %{x} == %{nil}' has both parts as nothing |
---|
426 | # which means '%if ==' is always True and '%if !=' is always false. |
---|
427 | # |
---|
428 | if ifls[0] == '==': |
---|
429 | istrue = True |
---|
430 | elif ifls[0] == '!=': |
---|
431 | istrue = False |
---|
432 | else: |
---|
433 | istrue = _check_bool(ifls[0]) |
---|
434 | if istrue == None: |
---|
435 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
436 | istrue = False |
---|
437 | elif len(ifls) == 2: |
---|
438 | if ifls[0] == '!': |
---|
439 | istrue = _check_bool(ifls[1]) |
---|
440 | if istrue == None: |
---|
441 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
442 | istrue = False |
---|
443 | else: |
---|
444 | istrue = not istrue |
---|
445 | else: |
---|
446 | # |
---|
447 | # Check is something is being checked against empty, |
---|
448 | # ie '%if %{x} == %{nil}' |
---|
449 | # The logic is 'something == nothing' is False and |
---|
450 | # 'something != nothing' is True. |
---|
451 | # |
---|
452 | if ifls[1] == '==': |
---|
453 | istrue = False |
---|
454 | elif ifls[1] == '!=': |
---|
455 | istrue = True |
---|
456 | else: |
---|
457 | self._error('invalid if bool operator: ' + reduce(add, ls, '')) |
---|
458 | elif len(ifls) == 3: |
---|
459 | if ifls[1] == '==': |
---|
460 | if ifls[0] == ifls[2]: |
---|
461 | istrue = True |
---|
462 | else: |
---|
463 | istrue = False |
---|
464 | elif ifls[1] == '!=' or ifls[1] == '=!': |
---|
465 | if ifls[0] != ifls[2]: |
---|
466 | istrue = True |
---|
467 | else: |
---|
468 | istrue = False |
---|
469 | elif ifls[1] == '>': |
---|
470 | if ifls[0] > ifls[2]: |
---|
471 | istrue = True |
---|
472 | else: |
---|
473 | istrue = False |
---|
474 | elif ifls[1] == '>=' or ifls[1] == '=>': |
---|
475 | if ifls[0] >= ifls[2]: |
---|
476 | istrue = True |
---|
477 | else: |
---|
478 | istrue = False |
---|
479 | elif ifls[1] == '<=' or ifls[1] == '=<': |
---|
480 | if ifls[0] <= ifls[2]: |
---|
481 | istrue = True |
---|
482 | else: |
---|
483 | istrue = False |
---|
484 | elif ifls[1] == '<': |
---|
485 | if ifls[0] < ifls[2]: |
---|
486 | istrue = True |
---|
487 | else: |
---|
488 | istrue = False |
---|
489 | else: |
---|
490 | self._error('invalid %if operator: ' + reduce(add, ls, '')) |
---|
491 | else: |
---|
492 | self._error('malformed if: ' + reduce(add, ls, '')) |
---|
493 | if invert: |
---|
494 | istrue = not istrue |
---|
495 | log.trace('config: %s: _if: %s %s' % (self.init_name, ifls, str(istrue))) |
---|
496 | return self._ifs(config, ls, '%if', istrue, isvalid, dir, info) |
---|
497 | |
---|
498 | def _ifos(self, config, ls, isvalid, dir, info): |
---|
499 | isos = False |
---|
500 | if isvalid: |
---|
501 | os = self.define('_os') |
---|
502 | for l in ls: |
---|
503 | if l in os: |
---|
504 | isos = True |
---|
505 | break |
---|
506 | return self._ifs(config, ls, '%ifos', isos, isvalid, dir, info) |
---|
507 | |
---|
508 | def _ifarch(self, config, positive, ls, isvalid, dir, info): |
---|
509 | isarch = False |
---|
510 | if isvalid: |
---|
511 | arch = self.define('_arch') |
---|
512 | for l in ls: |
---|
513 | if l in arch: |
---|
514 | isarch = True |
---|
515 | break |
---|
516 | if not positive: |
---|
517 | isarch = not isarch |
---|
518 | return self._ifs(config, ls, '%ifarch', isarch, isvalid, dir, info) |
---|
519 | |
---|
520 | def _parse(self, config, dir, info, roc = False, isvalid = True): |
---|
521 | # roc = return on control |
---|
522 | |
---|
523 | def _clean(line): |
---|
524 | line = line[0:-1] |
---|
525 | b = line.find('#') |
---|
526 | if b >= 0: |
---|
527 | line = line[1:b] |
---|
528 | return line.strip() |
---|
529 | |
---|
530 | # |
---|
531 | # Need to add code to count matching '{' and '}' and if they |
---|
532 | # do not match get the next line and add to the string until |
---|
533 | # they match. This closes an opening '{' that is on another |
---|
534 | # line. |
---|
535 | # |
---|
536 | for l in config: |
---|
537 | self.lc += 1 |
---|
538 | l = _clean(l) |
---|
539 | if len(l) == 0: |
---|
540 | continue |
---|
541 | log.trace('config: %s: %03d: %s %s' % \ |
---|
542 | (self.init_name, self.lc, str(isvalid), l)) |
---|
543 | lo = l |
---|
544 | if isvalid: |
---|
545 | l = self._expand(l) |
---|
546 | if len(l) == 0: |
---|
547 | continue |
---|
548 | if l[0] == '%': |
---|
549 | ls = self.wss.split(l, 2) |
---|
550 | los = self.wss.split(lo, 2) |
---|
551 | if ls[0] == '%disable': |
---|
552 | if isvalid: |
---|
553 | self._disable(config, ls) |
---|
554 | elif ls[0] == '%select': |
---|
555 | if isvalid: |
---|
556 | self._select(config, ls) |
---|
557 | elif ls[0] == '%error': |
---|
558 | if isvalid: |
---|
559 | return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))]) |
---|
560 | elif ls[0] == '%warning': |
---|
561 | if isvalid: |
---|
562 | return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))]) |
---|
563 | elif ls[0] == '%define' or ls[0] == '%global': |
---|
564 | if isvalid: |
---|
565 | self._define(config, ls) |
---|
566 | elif ls[0] == '%undefine': |
---|
567 | if isvalid: |
---|
568 | self._undefine(config, ls) |
---|
569 | elif ls[0] == '%if': |
---|
570 | d = self._if(config, ls, isvalid, dir, info) |
---|
571 | if len(d): |
---|
572 | log.trace('config: %s: %%if: %s' % (self.init_name, d)) |
---|
573 | return ('data', d) |
---|
574 | elif ls[0] == '%ifn': |
---|
575 | d = self._if(config, ls, isvalid, dir, info, True) |
---|
576 | if len(d): |
---|
577 | log.trace('config: %s: %%ifn: %s' % (self.init_name, d)) |
---|
578 | return ('data', d) |
---|
579 | elif ls[0] == '%ifos': |
---|
580 | d = self._ifos(config, ls, isvalid, dir, info) |
---|
581 | if len(d): |
---|
582 | return ('data', d) |
---|
583 | elif ls[0] == '%ifarch': |
---|
584 | d = self._ifarch(config, True, ls, isvalid, dir, info) |
---|
585 | if len(d): |
---|
586 | return ('data', d) |
---|
587 | elif ls[0] == '%ifnarch': |
---|
588 | d = self._ifarch(config, False, ls, isvalid, dir, info) |
---|
589 | if len(d): |
---|
590 | return ('data', d) |
---|
591 | elif ls[0] == '%endif': |
---|
592 | if roc: |
---|
593 | return ('control', '%endif', '%endif') |
---|
594 | log.warning("unexpected '" + ls[0] + "'") |
---|
595 | elif ls[0] == '%else': |
---|
596 | if roc: |
---|
597 | return ('control', '%else', '%else') |
---|
598 | log.warning("unexpected '" + ls[0] + "'") |
---|
599 | elif ls[0].startswith('%defattr'): |
---|
600 | return ('data', [l]) |
---|
601 | elif ls[0] == '%bcond_with': |
---|
602 | if isvalid: |
---|
603 | # |
---|
604 | # Check if already defined. Would be by the command line or |
---|
605 | # even a host specific default. |
---|
606 | # |
---|
607 | if self._label('with_' + ls[1]) not in self.macros: |
---|
608 | self._define(config, (ls[0], 'without_' + ls[1])) |
---|
609 | elif ls[0] == '%bcond_without': |
---|
610 | if isvalid: |
---|
611 | if self._label('without_' + ls[1]) not in self.macros: |
---|
612 | self._define(config, (ls[0], 'with_' + ls[1])) |
---|
613 | else: |
---|
614 | pt = self._parse_token(lo, los, l, ls) |
---|
615 | if pt is not None: |
---|
616 | return pt |
---|
617 | if self.ignores is not None: |
---|
618 | for r in self.ignores: |
---|
619 | if r.match(ls[0]) is not None: |
---|
620 | return ('data', [l]) |
---|
621 | if isvalid: |
---|
622 | for d in self.directives: |
---|
623 | if ls[0].strip() == d: |
---|
624 | return ('directive', ls[0].strip(), ls[1:]) |
---|
625 | log.warning("unknown directive: '" + ls[0] + "'") |
---|
626 | return ('data', [lo]) |
---|
627 | else: |
---|
628 | return ('data', [lo]) |
---|
629 | return ('control', '%end', '%end') |
---|
630 | |
---|
631 | def _parse_token(self, line, line_split, line_expanded, line_split_expanded): |
---|
632 | return None |
---|
633 | |
---|
634 | def _process_directive(self, results, directive, info, data): |
---|
635 | new_data = [] |
---|
636 | if results[1] == '%description': |
---|
637 | new_data = [' '.join(results[2])] |
---|
638 | else: |
---|
639 | directive, into, data = self._directive_filter(results, directive, info, data) |
---|
640 | if directive and directive != results[1]: |
---|
641 | self._directive_extend(directive, data) |
---|
642 | directive = results[1] |
---|
643 | data = new_data |
---|
644 | return (directive, info, data) |
---|
645 | |
---|
646 | def _process_data(self, results, directive, info, data): |
---|
647 | new_data = [] |
---|
648 | for l in results[1]: |
---|
649 | if l.startswith('%error'): |
---|
650 | l = self._expand(l) |
---|
651 | raise error.general('config error: %s' % (l[7:])) |
---|
652 | elif l.startswith('%warning'): |
---|
653 | l = self._expand(l) |
---|
654 | log.stderr('warning: %s' % (l[9:])) |
---|
655 | log.warning(l[9:]) |
---|
656 | if not directive: |
---|
657 | l = self._expand(l) |
---|
658 | ls = self.tags.split(l, 1) |
---|
659 | log.trace('config: %s: _tag: %s %s' % (self.init_name, l, ls)) |
---|
660 | if len(ls) > 1: |
---|
661 | info = ls[0].lower() |
---|
662 | if info[-1] == ':': |
---|
663 | info = info[:-1] |
---|
664 | info_data = ls[1].strip() |
---|
665 | else: |
---|
666 | info_data = ls[0].strip() |
---|
667 | if info is not None: |
---|
668 | self._info_append(info, info_data) |
---|
669 | else: |
---|
670 | log.warning("invalid format: '%s'" % (info_data[:-1])) |
---|
671 | else: |
---|
672 | log.trace('config: %s: _data: %s %s' % (self.init_name, l, new_data)) |
---|
673 | new_data.append(l) |
---|
674 | return (directive, info, data + new_data) |
---|
675 | |
---|
676 | def _process_block(self, results, directive, info, data): |
---|
677 | raise error.internal('known block type: %s' % (results[0])) |
---|
678 | |
---|
679 | def _directive_extend(self, dir, data): |
---|
680 | pass |
---|
681 | |
---|
682 | def _directive_filter(self, results, directive, info, data): |
---|
683 | return directive, into, data |
---|
684 | |
---|
685 | def _info_append(self, info, data): |
---|
686 | pass |
---|
687 | |
---|
688 | def load(self, name): |
---|
689 | |
---|
690 | def common_end(left, right): |
---|
691 | end = '' |
---|
692 | while len(left) and len(right): |
---|
693 | if left[-1] != right[-1]: |
---|
694 | return end |
---|
695 | end = left[-1] + end |
---|
696 | left = left[:-1] |
---|
697 | right = right[:-1] |
---|
698 | return end |
---|
699 | |
---|
700 | if self.load_depth == 0: |
---|
701 | self.in_error = False |
---|
702 | self.lc = 0 |
---|
703 | self.name = name |
---|
704 | self.conditionals = {} |
---|
705 | |
---|
706 | self.load_depth += 1 |
---|
707 | |
---|
708 | save_name = self.name |
---|
709 | save_lc = self.lc |
---|
710 | |
---|
711 | self.name = name |
---|
712 | self.lc = 0 |
---|
713 | |
---|
714 | # |
---|
715 | # Locate the config file. Expand any macros then add the |
---|
716 | # extension. Check if the file exists, therefore directly |
---|
717 | # referenced. If not see if the file contains ':' or the path |
---|
718 | # separator. If it does split the path else use the standard config dir |
---|
719 | # path in the defaults. |
---|
720 | # |
---|
721 | exname = self.expand(name) |
---|
722 | |
---|
723 | # |
---|
724 | # Macro could add an extension. |
---|
725 | # |
---|
726 | if exname.endswith('.cfg'): |
---|
727 | configname = exname |
---|
728 | else: |
---|
729 | configname = '%s.cfg' % (exname) |
---|
730 | name = '%s.cfg' % (name) |
---|
731 | |
---|
732 | if ':' in configname: |
---|
733 | cfgname = path.basename(configname) |
---|
734 | else: |
---|
735 | cfgname = common_end(configname, name) |
---|
736 | |
---|
737 | if not path.exists(configname): |
---|
738 | if ':' in configname: |
---|
739 | configdirs = path.dirname(configname).split(':') |
---|
740 | else: |
---|
741 | configdirs = self.define('_configdir').split(':') |
---|
742 | for cp in configdirs: |
---|
743 | configname = path.join(path.abspath(cp), cfgname) |
---|
744 | if path.exists(configname): |
---|
745 | break |
---|
746 | configname = None |
---|
747 | if configname is None: |
---|
748 | raise error.general('no config file found: %s' % (cfgname)) |
---|
749 | |
---|
750 | try: |
---|
751 | log.trace('config: %s: _open: %s' % (self.init_name, path.host(configname))) |
---|
752 | config = open(path.host(configname), 'r') |
---|
753 | except IOError, err: |
---|
754 | raise error.general('error opening config file: %s' % (path.host(configname))) |
---|
755 | self.configpath += [configname] |
---|
756 | |
---|
757 | self._includes += [configname] |
---|
758 | |
---|
759 | try: |
---|
760 | dir = None |
---|
761 | info = None |
---|
762 | data = [] |
---|
763 | while True: |
---|
764 | r = self._parse(config, dir, info) |
---|
765 | if r[0] == 'control': |
---|
766 | if r[1] == '%end': |
---|
767 | break |
---|
768 | log.warning("unexpected '%s'" % (r[1])) |
---|
769 | elif r[0] == 'directive': |
---|
770 | if r[1] == '%include': |
---|
771 | self.load(r[2][0]) |
---|
772 | continue |
---|
773 | dir, info, data = self._process_directive(r, dir, info, data) |
---|
774 | elif r[0] == 'data': |
---|
775 | dir, info, data = self._process_data(r, dir, info, data) |
---|
776 | else: |
---|
777 | self._error("%d: invalid parse state: '%s" % (self.lc, r[0])) |
---|
778 | if dir is not None: |
---|
779 | self._directive_extend(dir, data) |
---|
780 | except: |
---|
781 | config.close() |
---|
782 | raise |
---|
783 | |
---|
784 | config.close() |
---|
785 | |
---|
786 | self.name = save_name |
---|
787 | self.lc = save_lc |
---|
788 | |
---|
789 | self.load_depth -= 1 |
---|
790 | |
---|
791 | def defined(self, name): |
---|
792 | return self.macros.has_key(name) |
---|
793 | |
---|
794 | def define(self, name): |
---|
795 | if name in self.macros: |
---|
796 | d = self.macros[name] |
---|
797 | else: |
---|
798 | n = self._label(name) |
---|
799 | if n in self.macros: |
---|
800 | d = self.macros[n] |
---|
801 | else: |
---|
802 | raise error.general('%d: macro "%s" not found' % (self.lc, name)) |
---|
803 | return self._expand(d) |
---|
804 | |
---|
805 | def set_define(self, name, value): |
---|
806 | self.macros[name] = value |
---|
807 | |
---|
808 | def expand(self, line): |
---|
809 | if type(line) == list: |
---|
810 | el = [] |
---|
811 | for l in line: |
---|
812 | el += [self._expand(l)] |
---|
813 | return el |
---|
814 | return self._expand(line) |
---|
815 | |
---|
816 | def macro(self, name): |
---|
817 | if name in self.macros: |
---|
818 | return self.macros[name] |
---|
819 | raise error.general('macro "%s" not found' % (name)) |
---|
820 | |
---|
821 | def directive(self, name): |
---|
822 | pass |
---|
823 | |
---|
824 | def abspath(self, rpath): |
---|
825 | return path.abspath(self.define(rpath)) |
---|
826 | |
---|
827 | def includes(self): |
---|
828 | return self._includes |
---|
829 | |
---|
830 | def file_name(self): |
---|
831 | return self.init_name |
---|
832 | |
---|
833 | def run(): |
---|
834 | import sys |
---|
835 | try: |
---|
836 | # |
---|
837 | # Run where defaults.mc is located |
---|
838 | # |
---|
839 | opts = options.load(sys.argv, defaults = 'defaults.mc') |
---|
840 | log.trace('config: count %d' % (len(opts.config_files()))) |
---|
841 | for config_file in opts.config_files(): |
---|
842 | s = file(config_file, opts) |
---|
843 | print s |
---|
844 | del s |
---|
845 | except error.general, gerr: |
---|
846 | print gerr |
---|
847 | sys.exit(1) |
---|
848 | except error.internal, ierr: |
---|
849 | print ierr |
---|
850 | sys.exit(1) |
---|
851 | except KeyboardInterrupt: |
---|
852 | log.notice('abort: user terminated') |
---|
853 | sys.exit(1) |
---|
854 | sys.exit(0) |
---|
855 | |
---|
856 | if __name__ == "__main__": |
---|
857 | run() |
---|