1 | # |
---|
2 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
3 | # Copyright 2010-2018 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 | # Permission to use, copy, modify, and/or distribute this software for any |
---|
9 | # purpose with or without fee is hereby granted, provided that the above |
---|
10 | # copyright notice and this permission notice appear in all copies. |
---|
11 | # |
---|
12 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
13 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
14 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
15 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
16 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
17 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
18 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
19 | |
---|
20 | # |
---|
21 | # This code is based on a tool I wrote to parse RPM spec files in the RTEMS |
---|
22 | # project. This is now a configuration file format that has moved away from the |
---|
23 | # spec file format to support the specific needs of cross-compiling GCC. This |
---|
24 | # module parses a configuration file into Python data types that can be used by |
---|
25 | # other software modules. |
---|
26 | # |
---|
27 | |
---|
28 | from __future__ import print_function |
---|
29 | |
---|
30 | import copy |
---|
31 | from functools import reduce |
---|
32 | import os |
---|
33 | import re |
---|
34 | import sys |
---|
35 | |
---|
36 | try: |
---|
37 | import error |
---|
38 | import execute |
---|
39 | import log |
---|
40 | import options |
---|
41 | import path |
---|
42 | import pkgconfig |
---|
43 | import sources |
---|
44 | except KeyboardInterrupt: |
---|
45 | print('user terminated', file = sys.stderr) |
---|
46 | sys.exit(1) |
---|
47 | except: |
---|
48 | print('error: unknown application load error', file = sys.stderr) |
---|
49 | sys.exit(1) |
---|
50 | |
---|
51 | def _check_bool(value): |
---|
52 | istrue = None |
---|
53 | if value.isdigit(): |
---|
54 | if int(value) == 0: |
---|
55 | istrue = False |
---|
56 | else: |
---|
57 | istrue = True |
---|
58 | else: |
---|
59 | if type(value) is str and len(value) == 2 and value[0] == '!': |
---|
60 | istrue = _check_bool(value[1]) |
---|
61 | if type(istrue) is bool: |
---|
62 | istrue = not istrue |
---|
63 | return istrue |
---|
64 | |
---|
65 | def _check_nil(value): |
---|
66 | if len(value): |
---|
67 | istrue = True |
---|
68 | else: |
---|
69 | istrue = False |
---|
70 | return istrue |
---|
71 | |
---|
72 | class package: |
---|
73 | |
---|
74 | def __init__(self, name, arch, config): |
---|
75 | self._name = name |
---|
76 | self._arch = arch |
---|
77 | self.config = config |
---|
78 | self.directives = {} |
---|
79 | self.infos = {} |
---|
80 | self.sizes = {} |
---|
81 | |
---|
82 | def __str__(self): |
---|
83 | |
---|
84 | def _dictlist(dl): |
---|
85 | s = '' |
---|
86 | dll = list(dl.keys()) |
---|
87 | dll.sort() |
---|
88 | for d in dll: |
---|
89 | if d: |
---|
90 | s += ' ' + d + ':\n' |
---|
91 | for l in dl[d]: |
---|
92 | s += ' ' + l + '\n' |
---|
93 | return s |
---|
94 | |
---|
95 | s = '\npackage: ' + self._name + \ |
---|
96 | '\n directives:\n' + _dictlist(self.directives) + \ |
---|
97 | '\n infos:\n' + _dictlist(self.infos) |
---|
98 | |
---|
99 | return s |
---|
100 | |
---|
101 | def _macro_override(self, info, macro): |
---|
102 | '''See if a macro overrides this setting.''' |
---|
103 | overridden = self.config.macros.overridden(macro) |
---|
104 | if overridden: |
---|
105 | return self.config.macros.expand(macro) |
---|
106 | return info |
---|
107 | |
---|
108 | def directive_extend(self, dir, data): |
---|
109 | if dir not in self.directives: |
---|
110 | self.directives[dir] = [] |
---|
111 | for i in range(0, len(data)): |
---|
112 | data[i] = data[i].strip() |
---|
113 | self.directives[dir].extend(data) |
---|
114 | self.config.macros[dir] = '\n'.join(self.directives[dir]) |
---|
115 | |
---|
116 | def info_append(self, info, data): |
---|
117 | if info not in self.infos: |
---|
118 | self.infos[info] = [] |
---|
119 | self.infos[info].append(data) |
---|
120 | self.config.macros[info] = '\n'.join(self.infos[info]) |
---|
121 | |
---|
122 | def get_info(self, info, expand = True): |
---|
123 | if info in self.config.macros: |
---|
124 | _info = self.config.macros[info].split('\n') |
---|
125 | if expand: |
---|
126 | return self.config.expand(_info) |
---|
127 | else: |
---|
128 | return _info |
---|
129 | return None |
---|
130 | |
---|
131 | def extract_info(self, label, expand = True): |
---|
132 | ll = label.lower() |
---|
133 | infos = {} |
---|
134 | keys = self.config.macros.find('%s.*' % (ll)) |
---|
135 | for k in keys: |
---|
136 | if k == ll: |
---|
137 | k = '%s0' % (ll) |
---|
138 | elif not k[len(ll):].isdigit(): |
---|
139 | continue |
---|
140 | infos[k] = [self.config.expand(self.config.macros[k])] |
---|
141 | return infos |
---|
142 | |
---|
143 | def _find_macro(self, label, expand = True): |
---|
144 | if label in self.config.macros: |
---|
145 | macro = self.config.macros[label].split('\n') |
---|
146 | if expand: |
---|
147 | return self.config.expand(macro) |
---|
148 | else: |
---|
149 | return macro |
---|
150 | return None |
---|
151 | |
---|
152 | def find_info(self, label, expand = True): |
---|
153 | return self._find_macro(label, expand) |
---|
154 | |
---|
155 | def find_directive(self, label, expand = True): |
---|
156 | return self._find_macro(label, expand) |
---|
157 | |
---|
158 | def name(self): |
---|
159 | info = self.find_info('name') |
---|
160 | if info: |
---|
161 | n = info[0] |
---|
162 | else: |
---|
163 | n = self._name |
---|
164 | return self._macro_override(n, 'name') |
---|
165 | |
---|
166 | def summary(self): |
---|
167 | info = self.find_info('summary') |
---|
168 | if info: |
---|
169 | return info[0] |
---|
170 | return '' |
---|
171 | |
---|
172 | def url(self): |
---|
173 | info = self.find_info('url') |
---|
174 | if info: |
---|
175 | return info[0] |
---|
176 | return '' |
---|
177 | |
---|
178 | def version(self): |
---|
179 | info = self.find_info('version') |
---|
180 | if not info: |
---|
181 | return None |
---|
182 | return info[0] |
---|
183 | |
---|
184 | def release(self): |
---|
185 | info = self.find_info('release') |
---|
186 | if not info: |
---|
187 | return None |
---|
188 | return info[0] |
---|
189 | |
---|
190 | def buildarch(self): |
---|
191 | info = self.find_info('buildarch') |
---|
192 | if not info: |
---|
193 | return self._arch |
---|
194 | return info[0] |
---|
195 | |
---|
196 | def sources(self): |
---|
197 | return self.extract_info('source') |
---|
198 | |
---|
199 | def patches(self): |
---|
200 | return self.extract_info('patch') |
---|
201 | |
---|
202 | def prep(self): |
---|
203 | return self.find_directive('%prep') |
---|
204 | |
---|
205 | def build(self): |
---|
206 | return self.find_directive('%build') |
---|
207 | |
---|
208 | def install(self): |
---|
209 | return self.find_directive('%install') |
---|
210 | |
---|
211 | def clean(self): |
---|
212 | return self.find_directive('%clean') |
---|
213 | |
---|
214 | def include(self): |
---|
215 | return self.find_directive('%include') |
---|
216 | |
---|
217 | def testing(self): |
---|
218 | return self.find_directive('%testing') |
---|
219 | |
---|
220 | def long_name(self): |
---|
221 | return self.name() |
---|
222 | |
---|
223 | def disabled(self): |
---|
224 | return len(self.name()) == 0 |
---|
225 | |
---|
226 | def set_size(self, what, path_): |
---|
227 | if what not in self.sizes: |
---|
228 | self.sizes[what] = 0 |
---|
229 | self.sizes[what] += path.get_size(path_) |
---|
230 | |
---|
231 | def get_size(self, what): |
---|
232 | if what in self.sizes: |
---|
233 | return self.sizes[what] |
---|
234 | return 0 |
---|
235 | |
---|
236 | class file: |
---|
237 | """Parse a config file.""" |
---|
238 | |
---|
239 | _directive = [ '%include', |
---|
240 | '%description', |
---|
241 | '%prep', |
---|
242 | '%build', |
---|
243 | '%clean', |
---|
244 | '%install', |
---|
245 | '%testing' ] |
---|
246 | |
---|
247 | _ignore = [ re.compile('%setup'), |
---|
248 | re.compile('%configure'), |
---|
249 | re.compile('%source'), |
---|
250 | re.compile('%patch'), |
---|
251 | re.compile('%hash'), |
---|
252 | re.compile('%select'), |
---|
253 | re.compile('%disable') ] |
---|
254 | |
---|
255 | def __init__(self, name, opts, macros = None): |
---|
256 | log.trace('config: %s: initialising' % (name)) |
---|
257 | self.opts = opts |
---|
258 | self.init_name = name |
---|
259 | self.wss = re.compile(r'\s+') |
---|
260 | self.tags = re.compile(r':+') |
---|
261 | self.sf = re.compile(r'%\([^\)]+\)') |
---|
262 | self.set_macros(macros) |
---|
263 | self._reset(name) |
---|
264 | self.load(name) |
---|
265 | |
---|
266 | def __str__(self): |
---|
267 | |
---|
268 | def _dict(dd): |
---|
269 | s = '' |
---|
270 | ddl = list(dd.keys()) |
---|
271 | ddl.sort() |
---|
272 | for d in ddl: |
---|
273 | s += ' ' + d + ': ' + dd[d] + '\n' |
---|
274 | return s |
---|
275 | |
---|
276 | s = 'config: %s' % ('.'.join(self.configpath)) + \ |
---|
277 | '\n' + str(self.opts) + \ |
---|
278 | '\nlines parsed: %d' % (self.lc) + \ |
---|
279 | '\nname: ' + self.name + \ |
---|
280 | '\nmacros:\n' + str(self.macros) |
---|
281 | for _package in self._packages: |
---|
282 | s += str(self._packages[_package]) |
---|
283 | return s |
---|
284 | |
---|
285 | def _reset(self, name): |
---|
286 | self.name = name |
---|
287 | self.load_depth = 0 |
---|
288 | self.configpath = [] |
---|
289 | self._includes = [] |
---|
290 | self._packages = {} |
---|
291 | self.in_error = False |
---|
292 | self.lc = 0 |
---|
293 | self.if_depth = 0 |
---|
294 | self.conditionals = {} |
---|
295 | self._packages = {} |
---|
296 | self.package = 'main' |
---|
297 | self.disable_macro_reassign = False |
---|
298 | self.pkgconfig_prefix = None |
---|
299 | self.pkgconfig_crosscompile = False |
---|
300 | self.pkgconfig_filter_flags = False |
---|
301 | for arg in self.opts.args: |
---|
302 | if arg.startswith('--with-') or arg.startswith('--without-'): |
---|
303 | if '=' in arg: |
---|
304 | label, value = arg.split('=', 1) |
---|
305 | else: |
---|
306 | label = arg |
---|
307 | value = None |
---|
308 | label = label[2:].lower().replace('-', '_') |
---|
309 | if value: |
---|
310 | self.macros.define(label, value) |
---|
311 | else: |
---|
312 | self.macros.define(label) |
---|
313 | |
---|
314 | def _relative_path(self, p): |
---|
315 | sbdir = None |
---|
316 | if '_sbdir' in self.macros: |
---|
317 | sbdir = path.dirname(self.expand('%{_sbdir}')) |
---|
318 | if p.startswith(sbdir): |
---|
319 | p = p[len(sbdir) + 1:] |
---|
320 | return p |
---|
321 | |
---|
322 | def _name_line_msg(self, msg): |
---|
323 | return '%s:%d: %s' % (path.basename(self.name), self.lc, msg) |
---|
324 | |
---|
325 | def _output(self, text): |
---|
326 | if not self.opts.quiet(): |
---|
327 | log.output(text) |
---|
328 | |
---|
329 | def _error(self, msg): |
---|
330 | if not self.opts.dry_run(): |
---|
331 | if self.opts.keep_going(): |
---|
332 | err = 'error: %s' % (self._name_line_msg(msg)) |
---|
333 | log.stderr(err) |
---|
334 | log.output(err) |
---|
335 | self.in_error = True |
---|
336 | log.stderr('warning: switched to dry run due to errors') |
---|
337 | self.opts.set_dry_run() |
---|
338 | raise error.general(self._name_line_msg(msg)) |
---|
339 | |
---|
340 | def _label(self, name): |
---|
341 | if name.startswith('%{') and name[-1] == '}': |
---|
342 | return name |
---|
343 | return '%{' + name.lower() + '}' |
---|
344 | |
---|
345 | def _cross_compile(self): |
---|
346 | _host = self.expand('%{_host}') |
---|
347 | _build = self.expand('%{_build}') |
---|
348 | return _host != _build |
---|
349 | |
---|
350 | def _candian_cross_compile(self): |
---|
351 | _host = self.expand('%{_host}') |
---|
352 | _build = self.expand('%{_build}') |
---|
353 | _target = self.expand('%{_target}') |
---|
354 | _alloc_cxc = self.defined('%{allow_cxc}') |
---|
355 | return _alloc_cxc and _host != _build and _host != _target |
---|
356 | |
---|
357 | def _macro_split(self, s): |
---|
358 | '''Split the string (s) up by macros. Only split on the |
---|
359 | outter level. Nested levels will need to split with futher calls.''' |
---|
360 | trace_me = False |
---|
361 | if trace_me: |
---|
362 | print('------------------------------------------------------') |
---|
363 | macros = [] |
---|
364 | nesting = [] |
---|
365 | has_braces = False |
---|
366 | c = 0 |
---|
367 | while c < len(s): |
---|
368 | if trace_me: |
---|
369 | print('ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting) |
---|
370 | # |
---|
371 | # We need to watch for shell type variables or the form '${var}' because |
---|
372 | # they can upset the brace matching. |
---|
373 | # |
---|
374 | if s[c] == '%' or s[c] == '$': |
---|
375 | start = s[c] |
---|
376 | c += 1 |
---|
377 | if c == len(s): |
---|
378 | continue |
---|
379 | # |
---|
380 | # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ? |
---|
381 | # |
---|
382 | if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'): |
---|
383 | continue |
---|
384 | elif not s[c].isspace(): |
---|
385 | # |
---|
386 | # If this is a shell macro and we are at the outter |
---|
387 | # level or is '$var' forget it and move on. |
---|
388 | # |
---|
389 | if start == '$' and (s[c] != '{' or len(nesting) == 0): |
---|
390 | continue |
---|
391 | if s[c] == '{': |
---|
392 | this_has_braces = True |
---|
393 | else: |
---|
394 | this_has_braces = False |
---|
395 | nesting.append((c - 1, has_braces)) |
---|
396 | has_braces = this_has_braces |
---|
397 | elif len(nesting) > 0: |
---|
398 | if s[c] == '}' or (s[c].isspace() and not has_braces): |
---|
399 | # |
---|
400 | # Can have '%{?test: something %more}' where the |
---|
401 | # nested %more ends with the '}' which also ends |
---|
402 | # the outter macro. |
---|
403 | # |
---|
404 | if not has_braces: |
---|
405 | if s[c] == '}': |
---|
406 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
407 | nesting = nesting[:-1] |
---|
408 | if len(nesting) == 0: |
---|
409 | macros.append(s[macro_start:c].strip()) |
---|
410 | if len(nesting) > 0: |
---|
411 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
412 | nesting = nesting[:-1] |
---|
413 | if len(nesting) == 0: |
---|
414 | macros.append(s[macro_start:c + 1].strip()) |
---|
415 | c += 1 |
---|
416 | if trace_me: |
---|
417 | print('ms:', macros) |
---|
418 | if trace_me: |
---|
419 | print('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') |
---|
420 | return macros |
---|
421 | |
---|
422 | def _shell(self, line, nesting = 0): |
---|
423 | # |
---|
424 | # Parse the line and handle nesting '()' pairs. If on Windows |
---|
425 | # handle embedded '"' (double quotes) as the command is run as |
---|
426 | # a double quoted string. |
---|
427 | # |
---|
428 | def _exec(shell_macro): |
---|
429 | output = '' |
---|
430 | if len(shell_macro) > 3: |
---|
431 | e = execute.capture_execution() |
---|
432 | if options.host_windows: |
---|
433 | shell_cmd = ''.join([c if c != '"' else '\\' + c for c in shell_macro[2:-1]]) |
---|
434 | cmd = '%s -c "%s"' % (self.macros.expand('%{__sh}'), shell_cmd) |
---|
435 | else: |
---|
436 | cmd = shell_macro[2:-1] |
---|
437 | exit_code, proc, output = e.shell(cmd) |
---|
438 | log.trace('shell-output: %d %s' % (exit_code, output)) |
---|
439 | if exit_code != 0: |
---|
440 | raise error.general('shell macro failed: %s: %d: %s' % (cmd, |
---|
441 | exit_code, |
---|
442 | output)) |
---|
443 | return output |
---|
444 | |
---|
445 | if nesting > 200: |
---|
446 | raise error.general('shell macro failed: too many nesting levels') |
---|
447 | |
---|
448 | updating = True |
---|
449 | while updating: |
---|
450 | updating = False |
---|
451 | pos = line.find('%(') |
---|
452 | if pos >= 0: |
---|
453 | braces = 0 |
---|
454 | for p in range(pos + 2, len(line)): |
---|
455 | if line[p] == '(': |
---|
456 | braces += 1 |
---|
457 | elif line[p] == ')': |
---|
458 | if braces > 0: |
---|
459 | braces -= 1 |
---|
460 | else: |
---|
461 | shell_cmd = '%(' + self._shell(line[pos + 2:p], nesting + 1) + ')' |
---|
462 | line = line[:pos] + _exec(shell_cmd) + line[p + 1:] |
---|
463 | updating = True |
---|
464 | break |
---|
465 | |
---|
466 | return line |
---|
467 | |
---|
468 | def _pkgconfig_check(self, test): |
---|
469 | # Hack to by pass pkgconfig checks when just wanting to download the |
---|
470 | # source. |
---|
471 | if self.macros['_dry_run'] == '1' and \ |
---|
472 | ('with_download' in self.macros and self.macros['with_download'] == '1'): |
---|
473 | return '0' |
---|
474 | ok = False |
---|
475 | log.trace('pkgconfig: check: crossc=%d pkg_crossc=%d prefix=%s' % ( self._cross_compile(), |
---|
476 | self.pkgconfig_crosscompile, |
---|
477 | self.pkgconfig_prefix)) |
---|
478 | log.trace('pkgconfig: check: test=%s' % (test)) |
---|
479 | if type(test) == str: |
---|
480 | test = test.split() |
---|
481 | if not self._cross_compile() or self.pkgconfig_crosscompile: |
---|
482 | try: |
---|
483 | pkg = pkgconfig.package(test[0], |
---|
484 | prefix = self.pkgconfig_prefix, |
---|
485 | output = self._output, |
---|
486 | src = log.trace) |
---|
487 | if len(test) != 1 and len(test) != 3: |
---|
488 | self._error('malformed check: %s' % (' '.join(test))) |
---|
489 | else: |
---|
490 | op = '>=' |
---|
491 | ver = '0' |
---|
492 | if len(test) == 3: |
---|
493 | op = test[1] |
---|
494 | ver = self.macros.expand(test[2]) |
---|
495 | ok = pkg.check(op, ver) |
---|
496 | except pkgconfig.error as pe: |
---|
497 | self._error('pkgconfig: check: %s' % (pe)) |
---|
498 | except: |
---|
499 | raise error.internal('pkgconfig failure') |
---|
500 | if ok: |
---|
501 | return '1' |
---|
502 | return '0' |
---|
503 | |
---|
504 | def _pkgconfig_flags(self, package, flags): |
---|
505 | pkg_flags = None |
---|
506 | if not self._cross_compile() or self.pkgconfig_crosscompile: |
---|
507 | try: |
---|
508 | pkg = pkgconfig.package(package, |
---|
509 | prefix = self.pkgconfig_prefix, |
---|
510 | output = self._output, |
---|
511 | src = log.trace) |
---|
512 | pkg_flags = pkg.get(flags) |
---|
513 | if pkg_flags and self.pkgconfig_filter_flags: |
---|
514 | fflags = [] |
---|
515 | for f in pkg_flags.split(): |
---|
516 | if not f.startswith('-W'): |
---|
517 | fflags += [f] |
---|
518 | pkg_flags = ' '.join(fflags) |
---|
519 | log.trace('pkgconfig: %s: %s' % (flags, pkg_flags)) |
---|
520 | except pkgconfig.error as pe: |
---|
521 | self._error('pkgconfig: %s: %s' % (flags, pe)) |
---|
522 | except: |
---|
523 | raise error.internal('pkgconfig failure') |
---|
524 | if pkg_flags is None: |
---|
525 | pkg_flags = '' |
---|
526 | return pkg_flags |
---|
527 | |
---|
528 | def _pkgconfig(self, pcl): |
---|
529 | ok = False |
---|
530 | ps = '' |
---|
531 | if pcl[0] == 'check': |
---|
532 | ps = self._pkgconfig_check(pcl[1:]) |
---|
533 | elif pcl[0] == 'prefix': |
---|
534 | if len(pcl) == 2: |
---|
535 | self.pkgconfig_prefix = pcl[1] |
---|
536 | else: |
---|
537 | self._error('prefix error: %s' % (' '.join(pcl))) |
---|
538 | elif pcl[0] == 'crosscompile': |
---|
539 | ok = True |
---|
540 | if len(pcl) == 2: |
---|
541 | if pcl[1].lower() == 'yes': |
---|
542 | self.pkgconfig_crosscompile = True |
---|
543 | elif pcl[1].lower() == 'no': |
---|
544 | self.pkgconfig_crosscompile = False |
---|
545 | else: |
---|
546 | ok = False |
---|
547 | else: |
---|
548 | ok = False |
---|
549 | if not ok: |
---|
550 | self._error('crosscompile error: %s' % (' '.join(pcl))) |
---|
551 | elif pcl[0] == 'filter-flags': |
---|
552 | ok = True |
---|
553 | if len(pcl) == 2: |
---|
554 | if pcl[1].lower() == 'yes': |
---|
555 | self.pkgconfig_filter_flags = True |
---|
556 | elif pcl[1].lower() == 'no': |
---|
557 | self.pkgconfig_filter_flags = False |
---|
558 | else: |
---|
559 | ok = False |
---|
560 | else: |
---|
561 | ok = False |
---|
562 | if not ok: |
---|
563 | self._error('crosscompile error: %s' % (' '.join(pcl))) |
---|
564 | elif pcl[0] in ['ccflags', 'cflags', 'ldflags', 'libs']: |
---|
565 | ps = self._pkgconfig_flags(pcl[1], pcl[0]) |
---|
566 | else: |
---|
567 | self._error('pkgconfig error: %s' % (' '.join(pcl))) |
---|
568 | return ps |
---|
569 | |
---|
570 | def _expand(self, s): |
---|
571 | expand_count = 0 |
---|
572 | expanded = True |
---|
573 | while expanded: |
---|
574 | expand_count += 1 |
---|
575 | if expand_count > 500: |
---|
576 | raise error.general('macro expand looping: %s' % (s)) |
---|
577 | expanded = False |
---|
578 | ms = self._macro_split(s) |
---|
579 | for m in ms: |
---|
580 | mn = m |
---|
581 | # |
---|
582 | # A macro can be '%{macro}' or '%macro'. Turn the later into |
---|
583 | # the former. |
---|
584 | # |
---|
585 | show_warning = True |
---|
586 | if mn[1] != '{': |
---|
587 | for r in self._ignore: |
---|
588 | if r.match(mn) is not None: |
---|
589 | mn = None |
---|
590 | break |
---|
591 | else: |
---|
592 | mn = self._label(mn[1:]) |
---|
593 | show_warning = False |
---|
594 | elif m.startswith('%{expand'): |
---|
595 | colon = m.find(':') |
---|
596 | if colon < 8: |
---|
597 | log.warning(self._name_line_msg('malformed expand macro, no colon found')) |
---|
598 | else: |
---|
599 | e = self._expand(m[colon + 1:-1].strip()) |
---|
600 | s = s.replace(m, self._label(e)) |
---|
601 | expanded = True |
---|
602 | mn = None |
---|
603 | elif m.startswith('%{with '): |
---|
604 | # |
---|
605 | # Change the ' ' to '_' because the macros have no spaces. |
---|
606 | # |
---|
607 | n = self._label('with_' + m[7:-1].strip()) |
---|
608 | if n in self.macros: |
---|
609 | s = s.replace(m, '1') |
---|
610 | else: |
---|
611 | s = s.replace(m, '0') |
---|
612 | expanded = True |
---|
613 | mn = None |
---|
614 | elif m.startswith('%{echo'): |
---|
615 | if not m.endswith('}'): |
---|
616 | log.warning(self._name_line_msg("malformed conditional macro '%s'" % (m))) |
---|
617 | mn = None |
---|
618 | else: |
---|
619 | e = self._expand(m[6:-1].strip()) |
---|
620 | log.notice('%s' % (self._name_line_msg(e))) |
---|
621 | s = '' |
---|
622 | expanded = True |
---|
623 | mn = None |
---|
624 | elif m.startswith('%{defined'): |
---|
625 | n = self._label(m[9:-1].strip()) |
---|
626 | if n in self.macros: |
---|
627 | s = s.replace(m, '1') |
---|
628 | else: |
---|
629 | s = s.replace(m, '0') |
---|
630 | expanded = True |
---|
631 | mn = None |
---|
632 | elif m.startswith('%{!defined'): |
---|
633 | n = self._label(m[10:-1].strip()) |
---|
634 | if n in self.macros: |
---|
635 | s = s.replace(m, '0') |
---|
636 | else: |
---|
637 | s = s.replace(m, '1') |
---|
638 | expanded = True |
---|
639 | mn = None |
---|
640 | elif m.startswith('%{triplet'): |
---|
641 | triplet = m[len('%{triplet'):-1].strip().split() |
---|
642 | ok = False |
---|
643 | if len(triplet) == 2: |
---|
644 | macro = self._expand(triplet[0]) |
---|
645 | value = self._expand(triplet[1]) |
---|
646 | vorig = value |
---|
647 | arch_value = '' |
---|
648 | vendor_value = '' |
---|
649 | os_value = '' |
---|
650 | dash = value.find('-') |
---|
651 | if dash >= 0: |
---|
652 | arch_value = value[:dash] |
---|
653 | value = value[dash + 1:] |
---|
654 | dash = value.find('-') |
---|
655 | if dash >= 0: |
---|
656 | vendor_value = value[:dash] |
---|
657 | value = value[dash + 1:] |
---|
658 | if len(value): |
---|
659 | os_value = value |
---|
660 | self.macros[macro] = vorig |
---|
661 | self.macros[macro + '_cpu'] = arch_value |
---|
662 | self.macros[macro + '_arch'] = arch_value |
---|
663 | self.macros[macro + '_vendor'] = vendor_value |
---|
664 | self.macros[macro + '_os'] = os_value |
---|
665 | ok = True |
---|
666 | if ok: |
---|
667 | s = s.replace(m, '') |
---|
668 | else: |
---|
669 | self._error('triplet error: %s' % (' '.join(triplet))) |
---|
670 | mn = None |
---|
671 | elif m.startswith('%{path '): |
---|
672 | pl = m[7:-1].strip().split() |
---|
673 | ok = False |
---|
674 | result = '' |
---|
675 | pl_0 = pl[0].lower() |
---|
676 | if pl_0 == 'prepend': |
---|
677 | if len(pl) == 2: |
---|
678 | ok = True |
---|
679 | p = ' '.join([self._expand(pp) for pp in pl[1:]]) |
---|
680 | if len(self.macros['_pathprepend']): |
---|
681 | self.macros['_pathprepend'] = \ |
---|
682 | '%s:%s' % (p, self.macros['_pathprepend']) |
---|
683 | else: |
---|
684 | self.macros['_pathprepend'] = p |
---|
685 | elif pl_0 == 'postpend': |
---|
686 | if len(pl) == 2: |
---|
687 | ok = True |
---|
688 | p = ' '.join([self._expand(pp) for pp in pl[1:]]) |
---|
689 | if len(self.macros['_pathprepend']): |
---|
690 | self.macros['_pathprepend'] = \ |
---|
691 | '%s:%s' % (self.macros['_pathprepend'], p) |
---|
692 | else: |
---|
693 | self.macros['_pathprepend'] = p |
---|
694 | elif pl_0 == 'check': |
---|
695 | if len(pl) == 3: |
---|
696 | pl_1 = pl[1].lower() |
---|
697 | p = ' '.join([self._expand(pp) for pp in pl[2:]]) |
---|
698 | if pl_1 == 'exists': |
---|
699 | ok = True |
---|
700 | if path.exists(p): |
---|
701 | result = '1' |
---|
702 | else: |
---|
703 | result = '0' |
---|
704 | elif pl_1 == 'isdir': |
---|
705 | ok = True |
---|
706 | if path.isdir(p): |
---|
707 | result = '1' |
---|
708 | else: |
---|
709 | result = '0' |
---|
710 | elif pl_1 == 'isfile': |
---|
711 | ok = True |
---|
712 | if path.isfile(p): |
---|
713 | result = '1' |
---|
714 | else: |
---|
715 | result = '0' |
---|
716 | if ok: |
---|
717 | s = s.replace(m, result) |
---|
718 | else: |
---|
719 | self._error('path error: %s' % (' '.join(pl))) |
---|
720 | mn = None |
---|
721 | elif m.startswith('%{pkgconfig '): |
---|
722 | pcl = m[11:-1].strip().split() |
---|
723 | if len(pcl): |
---|
724 | epcl = [] |
---|
725 | for pc in pcl: |
---|
726 | epcl += [self._expand(pc)] |
---|
727 | ps = self._pkgconfig(epcl) |
---|
728 | s = s.replace(m, ps) |
---|
729 | expanded = True |
---|
730 | else: |
---|
731 | self._error('pkgconfig error: %s' % (m[11:-1].strip())) |
---|
732 | mn = None |
---|
733 | elif m.startswith('%{?') or m.startswith('%{!?'): |
---|
734 | if m[2] == '!': |
---|
735 | start = 4 |
---|
736 | else: |
---|
737 | start = 3 |
---|
738 | colon = m[start:].find(':') |
---|
739 | if colon < 0: |
---|
740 | if not m.endswith('}'): |
---|
741 | log.warning(self._name_line_msg("malformed conditional macro '%s'" % (m))) |
---|
742 | mn = None |
---|
743 | else: |
---|
744 | mn = self._label(m[start:-1]) |
---|
745 | else: |
---|
746 | mn = self._label(m[start:start + colon]) |
---|
747 | if mn: |
---|
748 | if m.startswith('%{?'): |
---|
749 | istrue = False |
---|
750 | if mn in self.macros: |
---|
751 | # If defined and 0 or '' then it is false. |
---|
752 | istrue = _check_bool(self.macros[mn]) |
---|
753 | if istrue is None: |
---|
754 | istrue = _check_nil(self.macros[mn]) |
---|
755 | if colon >= 0 and istrue: |
---|
756 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
757 | expanded = True |
---|
758 | mn = None |
---|
759 | elif not istrue: |
---|
760 | mn = '%{nil}' |
---|
761 | else: |
---|
762 | isfalse = True |
---|
763 | if mn in self.macros: |
---|
764 | istrue = _check_bool(self.macros[mn]) |
---|
765 | if istrue is None or istrue == True: |
---|
766 | isfalse = False |
---|
767 | if colon >= 0 and isfalse: |
---|
768 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
769 | expanded = True |
---|
770 | mn = None |
---|
771 | else: |
---|
772 | mn = '%{nil}' |
---|
773 | if mn: |
---|
774 | if mn.lower() in self.macros: |
---|
775 | s = s.replace(m, self.macros[mn.lower()]) |
---|
776 | expanded = True |
---|
777 | elif show_warning: |
---|
778 | self._error("macro '%s' not found" % (mn)) |
---|
779 | return self._shell(s) |
---|
780 | |
---|
781 | def _disable(self, config, ls): |
---|
782 | if len(ls) != 2: |
---|
783 | log.warning(self._name_line_msg('invalid disable statement')) |
---|
784 | else: |
---|
785 | if ls[1] == 'select': |
---|
786 | self.macros.lock_read_map() |
---|
787 | log.trace('config: %s: %3d: _disable_select: %s' % (self.name, self.lc, |
---|
788 | ls[1])) |
---|
789 | else: |
---|
790 | log.warning(self._name_line_msg('invalid disable statement: %s' % (ls[1]))) |
---|
791 | |
---|
792 | def _select(self, config, ls): |
---|
793 | if len(ls) != 2: |
---|
794 | log.warning(self._name_line_msg('invalid select statement')) |
---|
795 | else: |
---|
796 | r = self.macros.set_read_map(ls[1]) |
---|
797 | log.trace('config: %s: %3d: _select: %s %s %r' % \ |
---|
798 | (self.name, self.lc, |
---|
799 | r, ls[1], self.macros.maps())) |
---|
800 | |
---|
801 | def _sources(self, ls): |
---|
802 | return sources.process(ls[0][1:], ls[1:], self.macros, self._error) |
---|
803 | |
---|
804 | def _hash(self, ls): |
---|
805 | return sources.hash(ls[1:], self.macros, self._error) |
---|
806 | |
---|
807 | def _define(self, config, ls): |
---|
808 | if len(ls) <= 1: |
---|
809 | log.warning(self._name_line_msg('invalid macro definition')) |
---|
810 | else: |
---|
811 | d = self._label(ls[1]) |
---|
812 | if self.disable_macro_reassign: |
---|
813 | if (d not in self.macros) or \ |
---|
814 | (d in self.macros and len(self.macros[d]) == 0): |
---|
815 | if len(ls) == 2: |
---|
816 | self.macros[d] = '1' |
---|
817 | else: |
---|
818 | self.macros[d] = ' '.join([f.strip() for f in ls[2:]]) |
---|
819 | else: |
---|
820 | log.warning(self._name_line_msg("macro '%s' already defined" % (d))) |
---|
821 | else: |
---|
822 | if len(ls) == 2: |
---|
823 | self.macros[d] = '1' |
---|
824 | else: |
---|
825 | self.macros[d] = ' '.join([f.strip() for f in ls[2:]]) |
---|
826 | |
---|
827 | def _undefine(self, config, ls): |
---|
828 | if len(ls) <= 1: |
---|
829 | log.warning(self._name_line_msg('invalid macro definition')) |
---|
830 | else: |
---|
831 | mn = self._label(ls[1]) |
---|
832 | if mn in self.macros: |
---|
833 | del self.macros[mn] |
---|
834 | |
---|
835 | def _ifs(self, config, ls, label, iftrue, isvalid, dir, info): |
---|
836 | log.trace('config: %s: %3d: _ifs[%i]: dir=%s %i %r' % \ |
---|
837 | (self.name, self.lc, self.if_depth, str(dir), len(ls), ls)) |
---|
838 | in_dir = dir |
---|
839 | in_iftrue = True |
---|
840 | data = [] |
---|
841 | while True: |
---|
842 | if isvalid and \ |
---|
843 | ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)): |
---|
844 | this_isvalid = True |
---|
845 | else: |
---|
846 | this_isvalid = False |
---|
847 | r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid) |
---|
848 | if r[0] == 'package': |
---|
849 | if this_isvalid: |
---|
850 | dir, info, data = self._process_package(r, dir, info, data) |
---|
851 | elif r[0] == 'control': |
---|
852 | if r[1] == '%end': |
---|
853 | self._error(label + ' without %endif') |
---|
854 | raise error.general('terminating build') |
---|
855 | if r[1] == '%endif': |
---|
856 | log.trace('config: %s: %3d: _ifs[%i]: %%endif: dir=%s %s %s %r' % \ |
---|
857 | (self.name, self.lc, self.if_depth, |
---|
858 | str(dir), r[1], this_isvalid, data)) |
---|
859 | if in_dir is None: |
---|
860 | if dir is not None: |
---|
861 | dir, info, data = self._process_directive(r, dir, info, data) |
---|
862 | else: |
---|
863 | if in_dir != dir: |
---|
864 | self._error('directives cannot change scope across if statements') |
---|
865 | |
---|
866 | return data |
---|
867 | if r[1] == '%else': |
---|
868 | in_iftrue = False |
---|
869 | elif r[0] == 'directive': |
---|
870 | if this_isvalid: |
---|
871 | if r[1] == '%include': |
---|
872 | self.load(r[2][0]) |
---|
873 | continue |
---|
874 | dir, info, data = self._process_directive(r, dir, info, data) |
---|
875 | elif r[0] == 'data': |
---|
876 | if this_isvalid: |
---|
877 | dir, info, data = self._process_data(r, dir, info, data) |
---|
878 | # @note is a directive extend missing |
---|
879 | |
---|
880 | def _if(self, config, ls, isvalid, dir, info, invert = False): |
---|
881 | |
---|
882 | def add(x, y): |
---|
883 | return x + ' ' + str(y) |
---|
884 | |
---|
885 | if len(ls) == 1: |
---|
886 | self._error('invalid if expression: ' + reduce(add, ls, '')) |
---|
887 | |
---|
888 | cistrue = True # compound istrue |
---|
889 | sls = reduce(add, ls[1:], '').split() |
---|
890 | cls = sls |
---|
891 | |
---|
892 | log.trace('config: %s: %3d: _if[%i]: %s' % (self.name, self.lc, |
---|
893 | self.if_depth, sls)) |
---|
894 | |
---|
895 | self.if_depth += 1 |
---|
896 | |
---|
897 | while len(cls) > 0 and isvalid: |
---|
898 | |
---|
899 | join_op = 'none' |
---|
900 | |
---|
901 | if cls[0] == '||' or cls[0] == '&&': |
---|
902 | if cls[0] == '||': |
---|
903 | join_op = 'or' |
---|
904 | elif cls[0] == '&&': |
---|
905 | join_op = 'and' |
---|
906 | cls = cls[1:] |
---|
907 | log.trace('config: %s: %3d: _if[%i]: joining: %s' % (self.name, self.lc, |
---|
908 | self.if_depth, |
---|
909 | join_op)) |
---|
910 | ori = 0 |
---|
911 | andi = 0 |
---|
912 | i = len(cls) |
---|
913 | if '||' in cls: |
---|
914 | ori = cls.index('||') |
---|
915 | log.trace('config: %s: %3d: _if[%i}: OR found at %i' % (self.name, self.lc, |
---|
916 | self.if_depth, |
---|
917 | ori)) |
---|
918 | if '&&' in cls: |
---|
919 | andi = cls.index('&&') |
---|
920 | log.trace('config: %s: %3d: _if[%i]: AND found at %i' % (self.name, self.lc, |
---|
921 | self.if_depth, |
---|
922 | andi)) |
---|
923 | if ori > 0 or andi > 0: |
---|
924 | if ori == 0: |
---|
925 | i = andi |
---|
926 | elif andi == 0: |
---|
927 | i = ori |
---|
928 | elif ori < andi: |
---|
929 | i = andi |
---|
930 | else: |
---|
931 | i = andi |
---|
932 | log.trace('config: %s: %3d: _if[%i]: next OP found at %i' % (self.name, self.lc, |
---|
933 | self.if_depth, |
---|
934 | i)) |
---|
935 | ls = cls[:i] |
---|
936 | if len(ls) == 0: |
---|
937 | self._error('invalid if expression: ' + reduce(add, sls, '')) |
---|
938 | cls = cls[i:] |
---|
939 | |
---|
940 | istrue = False |
---|
941 | |
---|
942 | s = ' '.join(ls) |
---|
943 | ifls = ls |
---|
944 | |
---|
945 | if len(ifls) == 1: |
---|
946 | # |
---|
947 | # Check if '%if %{x} == %{nil}' has both parts as nothing |
---|
948 | # which means '%if ==' is always True and '%if !=' is always false. |
---|
949 | # |
---|
950 | if ifls[0] == '==': |
---|
951 | istrue = True |
---|
952 | elif ifls[0] == '!=': |
---|
953 | istrue = False |
---|
954 | else: |
---|
955 | istrue = _check_bool(ifls[0]) |
---|
956 | if istrue == None: |
---|
957 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
958 | istrue = False |
---|
959 | elif len(ifls) == 2: |
---|
960 | if ifls[0] == '!': |
---|
961 | istrue = _check_bool(ifls[1]) |
---|
962 | if istrue == None: |
---|
963 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
964 | istrue = False |
---|
965 | else: |
---|
966 | istrue = not istrue |
---|
967 | else: |
---|
968 | # |
---|
969 | # Check is something is being checked against empty, |
---|
970 | # ie '%if %{x} == %{nil}' |
---|
971 | # The logic is 'something == nothing' is False and |
---|
972 | # 'something != nothing' is True. |
---|
973 | # |
---|
974 | if ifls[1] == '==': |
---|
975 | istrue = False |
---|
976 | elif ifls[1] == '!=': |
---|
977 | istrue = True |
---|
978 | else: |
---|
979 | self._error('invalid if bool operator: ' + reduce(add, ls, '')) |
---|
980 | else: |
---|
981 | if len(ifls) >= 3: |
---|
982 | for op in ['==', '!=', '>=', '=>', '=<', '<=', '>', '<']: |
---|
983 | if op in ifls: |
---|
984 | op_pos = ifls.index(op) |
---|
985 | ifls = (' '.join(ifls[:op_pos]), op, ' '.join(ifls[op_pos + 1:])) |
---|
986 | break |
---|
987 | if len(ifls) != 3: |
---|
988 | self._error('malformed if: ' + reduce(add, ls, '')) |
---|
989 | if ifls[1] == '==': |
---|
990 | if ifls[0] == ifls[2]: |
---|
991 | istrue = True |
---|
992 | else: |
---|
993 | istrue = False |
---|
994 | elif ifls[1] == '!=' or ifls[1] == '=!': |
---|
995 | if ifls[0] != ifls[2]: |
---|
996 | istrue = True |
---|
997 | else: |
---|
998 | istrue = False |
---|
999 | elif ifls[1] == '>': |
---|
1000 | if ifls[0] > ifls[2]: |
---|
1001 | istrue = True |
---|
1002 | else: |
---|
1003 | istrue = False |
---|
1004 | elif ifls[1] == '>=' or ifls[1] == '=>': |
---|
1005 | if ifls[0] >= ifls[2]: |
---|
1006 | istrue = True |
---|
1007 | else: |
---|
1008 | istrue = False |
---|
1009 | elif ifls[1] == '<=' or ifls[1] == '=<': |
---|
1010 | if ifls[0] <= ifls[2]: |
---|
1011 | istrue = True |
---|
1012 | else: |
---|
1013 | istrue = False |
---|
1014 | elif ifls[1] == '<': |
---|
1015 | if ifls[0] < ifls[2]: |
---|
1016 | istrue = True |
---|
1017 | else: |
---|
1018 | istrue = False |
---|
1019 | else: |
---|
1020 | self._error('invalid %if operator: ' + reduce(add, ls, '')) |
---|
1021 | |
---|
1022 | if join_op == 'or': |
---|
1023 | if istrue: |
---|
1024 | cistrue = True |
---|
1025 | elif join_op == 'and': |
---|
1026 | if not istrue: |
---|
1027 | cistrue = False |
---|
1028 | else: |
---|
1029 | cistrue = istrue |
---|
1030 | |
---|
1031 | log.trace('config: %s: %3d: _if[%i]: %s %s %s %s' % (self.name, self.lc, |
---|
1032 | self.if_depth, |
---|
1033 | ifls, str(cistrue), |
---|
1034 | join_op, str(istrue))) |
---|
1035 | |
---|
1036 | if invert: |
---|
1037 | cistrue = not cistrue |
---|
1038 | |
---|
1039 | ifs_return = self._ifs(config, ls, '%if', cistrue, isvalid, dir, info) |
---|
1040 | |
---|
1041 | self.if_depth -= 1 |
---|
1042 | |
---|
1043 | log.trace('config: %s: %3d: _if[%i]: %r' % (self.name, self.lc, |
---|
1044 | self.if_depth, ifs_return)) |
---|
1045 | |
---|
1046 | return ifs_return |
---|
1047 | |
---|
1048 | def _ifos(self, config, ls, isvalid, dir, info): |
---|
1049 | isos = False |
---|
1050 | if isvalid: |
---|
1051 | os = self.define('_os') |
---|
1052 | ls = ' '.join(ls).split() |
---|
1053 | for l in ls[1:]: |
---|
1054 | if l in os: |
---|
1055 | isos = True |
---|
1056 | break |
---|
1057 | return self._ifs(config, ls, '%ifos', isos, isvalid, dir, info) |
---|
1058 | |
---|
1059 | def _ifnos(self, config, ls, isvalid, dir, info): |
---|
1060 | isnos = True |
---|
1061 | if isvalid: |
---|
1062 | os = self.define('_os') |
---|
1063 | ls = ' '.join(ls).split() |
---|
1064 | for l in ls[1:]: |
---|
1065 | if l in os: |
---|
1066 | isnos = False |
---|
1067 | break |
---|
1068 | return self._ifs(config, ls, '%ifnos', isnos, isvalid, dir, info) |
---|
1069 | |
---|
1070 | def _ifarch(self, config, positive, ls, isvalid, dir, info): |
---|
1071 | isarch = False |
---|
1072 | if isvalid: |
---|
1073 | arch = self.define('_arch') |
---|
1074 | ls = ' '.join(ls).split() |
---|
1075 | for l in ls[1:]: |
---|
1076 | if l in arch: |
---|
1077 | isarch = True |
---|
1078 | break |
---|
1079 | if not positive: |
---|
1080 | isarch = not isarch |
---|
1081 | return self._ifs(config, ls, '%ifarch', isarch, isvalid, dir, info) |
---|
1082 | |
---|
1083 | def _parse(self, config, dir, info, roc = False, isvalid = True): |
---|
1084 | # roc = return on control |
---|
1085 | |
---|
1086 | def _clean(line): |
---|
1087 | line = line[0:-1] |
---|
1088 | b = line.find('#') |
---|
1089 | if b >= 0: |
---|
1090 | line = line[1:b] + ('\\' if line[-1] == '\\' else '') |
---|
1091 | return line.strip() |
---|
1092 | |
---|
1093 | def _clean_and_pack(line, last_line): |
---|
1094 | leading_ws = ' ' if len(line) > 0 and line[0].isspace() else '' |
---|
1095 | line = _clean(line) |
---|
1096 | if len(last_line) > 0: |
---|
1097 | line = last_line + leading_ws + line |
---|
1098 | return line |
---|
1099 | |
---|
1100 | # |
---|
1101 | # Need to add code to count matching '{' and '}' and if they |
---|
1102 | # do not match get the next line and add to the string until |
---|
1103 | # they match. This closes an opening '{' that is on another |
---|
1104 | # line. |
---|
1105 | # |
---|
1106 | ll = '' |
---|
1107 | for l in config: |
---|
1108 | self.lc += 1 |
---|
1109 | l = _clean_and_pack(l, ll) |
---|
1110 | if len(l) == 0: |
---|
1111 | continue |
---|
1112 | if l[-1] == '\\': |
---|
1113 | ll = l[0:-1] |
---|
1114 | continue |
---|
1115 | ll = '' |
---|
1116 | if isvalid: |
---|
1117 | indicator = '>' |
---|
1118 | else: |
---|
1119 | indicator = ' ' |
---|
1120 | log.trace('config: %s: %3d:%s%s [%s]' % \ |
---|
1121 | (self.name, self.lc, indicator, l, str(isvalid))) |
---|
1122 | lo = l |
---|
1123 | if isvalid: |
---|
1124 | l = self._expand(l) |
---|
1125 | if len(l) == 0: |
---|
1126 | continue |
---|
1127 | if l[0] == '%': |
---|
1128 | ls = self.wss.split(l, 2) |
---|
1129 | los = self.wss.split(lo, 2) |
---|
1130 | if ls[0] == '%package': |
---|
1131 | if isvalid: |
---|
1132 | if ls[1] == '-n': |
---|
1133 | name = ls[2] |
---|
1134 | else: |
---|
1135 | name = self.name + '-' + ls[1] |
---|
1136 | return ('package', name) |
---|
1137 | elif ls[0] == '%disable': |
---|
1138 | if isvalid: |
---|
1139 | self._disable(config, ls) |
---|
1140 | elif ls[0] == '%select': |
---|
1141 | if isvalid: |
---|
1142 | self._select(config, ls) |
---|
1143 | elif ls[0] == '%source' or ls[0] == '%patch': |
---|
1144 | if isvalid: |
---|
1145 | d = self._sources(ls) |
---|
1146 | if d is not None: |
---|
1147 | return ('data', d) |
---|
1148 | elif ls[0] == '%hash': |
---|
1149 | if isvalid: |
---|
1150 | d = self._hash(ls) |
---|
1151 | if d is not None: |
---|
1152 | return ('data', d) |
---|
1153 | elif ls[0] == '%patch': |
---|
1154 | if isvalid: |
---|
1155 | self._select(config, ls) |
---|
1156 | elif ls[0] == '%error': |
---|
1157 | if isvalid: |
---|
1158 | return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))]) |
---|
1159 | elif ls[0] == '%log': |
---|
1160 | if isvalid: |
---|
1161 | return ('data', ['%%log %s' % (self._name_line_msg(l[4:]))]) |
---|
1162 | elif ls[0] == '%warning': |
---|
1163 | if isvalid: |
---|
1164 | return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))]) |
---|
1165 | elif ls[0] == '%define' or ls[0] == '%global': |
---|
1166 | if isvalid: |
---|
1167 | self._define(config, ls) |
---|
1168 | elif ls[0] == '%undefine': |
---|
1169 | if isvalid: |
---|
1170 | self._undefine(config, ls) |
---|
1171 | elif ls[0] == '%if': |
---|
1172 | d = self._if(config, ls, isvalid, dir, info) |
---|
1173 | if len(d): |
---|
1174 | log.trace('config: %s: %3d: %%if: %s' % (self.name, self.lc, d)) |
---|
1175 | return ('data', d) |
---|
1176 | elif ls[0] == '%ifn': |
---|
1177 | d = self._if(config, ls, isvalid, dir, info, True) |
---|
1178 | if len(d): |
---|
1179 | log.trace('config: %s: %3d: %%ifn: %s' % (self.name, self.lc, d)) |
---|
1180 | return ('data', d) |
---|
1181 | elif ls[0] == '%ifos': |
---|
1182 | d = self._ifos(config, ls, isvalid, dir, info) |
---|
1183 | if len(d): |
---|
1184 | return ('data', d) |
---|
1185 | elif ls[0] == '%ifnos': |
---|
1186 | d = self._ifnos(config, ls, isvalid, dir, info) |
---|
1187 | if len(d): |
---|
1188 | return ('data', d) |
---|
1189 | elif ls[0] == '%ifarch': |
---|
1190 | d = self._ifarch(config, True, ls, isvalid, dir, info) |
---|
1191 | if len(d): |
---|
1192 | return ('data', d) |
---|
1193 | elif ls[0] == '%ifnarch': |
---|
1194 | d = self._ifarch(config, False, ls, isvalid, dir, info) |
---|
1195 | if len(d): |
---|
1196 | return ('data', d) |
---|
1197 | elif ls[0] == '%endif': |
---|
1198 | if roc: |
---|
1199 | return ('control', '%endif', '%endif') |
---|
1200 | log.warning(self._name_line_msg("unexpected '" + ls[0] + "'")) |
---|
1201 | elif ls[0] == '%else': |
---|
1202 | if roc: |
---|
1203 | return ('control', '%else', '%else') |
---|
1204 | log.warning(self._name_line_msg("unexpected '" + ls[0] + "'")) |
---|
1205 | elif ls[0].startswith('%defattr'): |
---|
1206 | return ('data', [l]) |
---|
1207 | elif ls[0] == '%bcond_with': |
---|
1208 | if isvalid: |
---|
1209 | # |
---|
1210 | # Check if already defined. Would be by the command line or |
---|
1211 | # even a host specific default. |
---|
1212 | # |
---|
1213 | if self._label('with_' + ls[1]) not in self.macros: |
---|
1214 | self._define(config, (ls[0], 'without_' + ls[1])) |
---|
1215 | elif ls[0] == '%bcond_without': |
---|
1216 | if isvalid: |
---|
1217 | if self._label('without_' + ls[1]) not in self.macros: |
---|
1218 | self._define(config, (ls[0], 'with_' + ls[1])) |
---|
1219 | else: |
---|
1220 | for r in self._ignore: |
---|
1221 | if r.match(ls[0]) is not None: |
---|
1222 | return ('data', [l]) |
---|
1223 | if isvalid: |
---|
1224 | for d in self._directive: |
---|
1225 | if ls[0].strip() == d: |
---|
1226 | log.trace('config: %s: %3d: _parse: directive: %s' % \ |
---|
1227 | (self.name, self.lc, ls[0].strip())) |
---|
1228 | return ('directive', ls[0].strip(), ls[1:]) |
---|
1229 | log.warning(self._name_line_msg("unknown directive: '" + ls[0] + "'")) |
---|
1230 | return ('data', [lo]) |
---|
1231 | else: |
---|
1232 | return ('data', [lo]) |
---|
1233 | return ('control', '%end', '%end') |
---|
1234 | |
---|
1235 | def _process_package(self, results, directive, info, data): |
---|
1236 | self._set_package(results[1]) |
---|
1237 | directive = None |
---|
1238 | return (directive, info, data) |
---|
1239 | |
---|
1240 | def _process_directive(self, results, directive, info, data): |
---|
1241 | new_data = [] |
---|
1242 | if results[1] == '%description': |
---|
1243 | new_data = [' '.join(results[2])] |
---|
1244 | if len(results[2]) == 0: |
---|
1245 | _package = 'main' |
---|
1246 | elif len(results[2]) == 1: |
---|
1247 | _package = results[2][0] |
---|
1248 | else: |
---|
1249 | if results[2][0].strip() != '-n': |
---|
1250 | log.warning(self._name_line_msg("unknown directive option: '%s'" % (' '.join(results[2])))) |
---|
1251 | _package = results[2][1].strip() |
---|
1252 | self._set_package(_package) |
---|
1253 | if directive and directive != results[1]: |
---|
1254 | self._directive_extend(directive, data) |
---|
1255 | directive = results[1] |
---|
1256 | data = new_data |
---|
1257 | return (directive, info, data) |
---|
1258 | |
---|
1259 | def _process_data(self, results, directive, info, data): |
---|
1260 | log.trace('config: %s: %3d: _process_data: result=#%r# directive=#%s# info=#%r# data=#%r#' % \ |
---|
1261 | (self.name, self.lc, results, directive, info, data)) |
---|
1262 | new_data = [] |
---|
1263 | for l in results[1]: |
---|
1264 | if l.startswith('%error'): |
---|
1265 | l = self._expand(l) |
---|
1266 | raise error.general('config error: %s' % (l[7:])) |
---|
1267 | elif l.startswith('%log'): |
---|
1268 | l = self._expand(l) |
---|
1269 | log.output(l[4:]) |
---|
1270 | elif l.startswith('%warning'): |
---|
1271 | l = self._expand(l) |
---|
1272 | log.warning(self._name_line_msg(l[9:])) |
---|
1273 | if not directive: |
---|
1274 | l = self._expand(l) |
---|
1275 | ls = self.tags.split(l, 1) |
---|
1276 | log.trace('config: %s: %3d: _tag: %s %s' % (self.name, self.lc, l, ls)) |
---|
1277 | if len(ls) > 1: |
---|
1278 | info = ls[0].lower() |
---|
1279 | if info[-1] == ':': |
---|
1280 | info = info[:-1] |
---|
1281 | info_data = ls[1].strip() |
---|
1282 | else: |
---|
1283 | info_data = ls[0].strip() |
---|
1284 | if info is not None: |
---|
1285 | self._info_append(info, info_data) |
---|
1286 | else: |
---|
1287 | log.warning(self._name_line_msg("invalid format: '%s'" % (info_data[:-1]))) |
---|
1288 | else: |
---|
1289 | l = self._expand(l) |
---|
1290 | log.trace('config: %s: %3d: _data: %s %s' % (self.name, self.lc, l, new_data)) |
---|
1291 | new_data.append(l) |
---|
1292 | return (directive, info, data + new_data) |
---|
1293 | |
---|
1294 | def _set_package(self, _package): |
---|
1295 | if self.package == 'main' and \ |
---|
1296 | self._packages[self.package].name() != None: |
---|
1297 | if self._packages[self.package].name() == _package: |
---|
1298 | return |
---|
1299 | if _package not in self._packages: |
---|
1300 | self._packages[_package] = package(_package, |
---|
1301 | self.define('%{_arch}'), |
---|
1302 | self) |
---|
1303 | self.package = _package |
---|
1304 | |
---|
1305 | def _directive_extend(self, dir, data): |
---|
1306 | log.trace('config: %s: %3d: _directive_extend: %s: %r' % (self.name, self.lc, dir, data)) |
---|
1307 | self._packages[self.package].directive_extend(dir, data) |
---|
1308 | |
---|
1309 | def _info_append(self, info, data): |
---|
1310 | self._packages[self.package].info_append(info, data) |
---|
1311 | |
---|
1312 | def set_macros(self, macros): |
---|
1313 | if macros is None: |
---|
1314 | self.macros = opts.defaults |
---|
1315 | else: |
---|
1316 | self.macros = macros |
---|
1317 | |
---|
1318 | def load(self, name): |
---|
1319 | |
---|
1320 | def common_end(left, right): |
---|
1321 | end = '' |
---|
1322 | while len(left) and len(right): |
---|
1323 | if left[-1] != right[-1]: |
---|
1324 | return end |
---|
1325 | end = left[-1] + end |
---|
1326 | left = left[:-1] |
---|
1327 | right = right[:-1] |
---|
1328 | return end |
---|
1329 | |
---|
1330 | if self.load_depth == 0: |
---|
1331 | self._reset(name) |
---|
1332 | self._packages[self.package] = package(self.package, |
---|
1333 | self.define('%{_arch}'), |
---|
1334 | self) |
---|
1335 | |
---|
1336 | self.load_depth += 1 |
---|
1337 | |
---|
1338 | save_name = self.name |
---|
1339 | save_lc = self.lc |
---|
1340 | |
---|
1341 | # |
---|
1342 | # Locate the config file. Expand any macros then add the |
---|
1343 | # extension. Check if the file exists, therefore directly |
---|
1344 | # referenced. If not see if the file contains ':' or the path |
---|
1345 | # separator. If it does split the path else use the standard config dir |
---|
1346 | # path in the defaults. |
---|
1347 | # |
---|
1348 | |
---|
1349 | exname = self.expand(name) |
---|
1350 | |
---|
1351 | # |
---|
1352 | # Macro could add an extension. |
---|
1353 | # |
---|
1354 | if exname.endswith('.cfg'): |
---|
1355 | configname = exname |
---|
1356 | else: |
---|
1357 | configname = '%s.cfg' % (exname) |
---|
1358 | name = '%s.cfg' % (name) |
---|
1359 | |
---|
1360 | if ':' in configname: |
---|
1361 | cfgname = path.basename(configname) |
---|
1362 | else: |
---|
1363 | cfgname = common_end(configname, name) |
---|
1364 | |
---|
1365 | if not path.exists(configname): |
---|
1366 | if ':' in configname: |
---|
1367 | configdirs = path.dirname(configname).split(':') |
---|
1368 | else: |
---|
1369 | configdirs = self.define('_configdir').split(':') |
---|
1370 | for cp in configdirs: |
---|
1371 | configname = path.join(path.abspath(cp), cfgname) |
---|
1372 | if path.exists(configname): |
---|
1373 | break |
---|
1374 | configname = None |
---|
1375 | if configname is None: |
---|
1376 | raise error.general('no config file found: %s' % (cfgname)) |
---|
1377 | |
---|
1378 | try: |
---|
1379 | log.trace('config: %s: _open: %s' % (self.name, path.host(configname))) |
---|
1380 | config = open(path.host(configname), 'r') |
---|
1381 | except IOError as err: |
---|
1382 | raise error.general('error opening config file: %s' % (path.host(configname))) |
---|
1383 | |
---|
1384 | self.configpath += [configname] |
---|
1385 | self._includes += [configname] |
---|
1386 | |
---|
1387 | self.name = self._relative_path(configname) |
---|
1388 | self.lc = 0 |
---|
1389 | |
---|
1390 | try: |
---|
1391 | dir = None |
---|
1392 | info = None |
---|
1393 | data = [] |
---|
1394 | while True: |
---|
1395 | r = self._parse(config, dir, info) |
---|
1396 | if r[0] == 'package': |
---|
1397 | dir, info, data = self._process_package(r, dir, info, data) |
---|
1398 | elif r[0] == 'control': |
---|
1399 | if r[1] == '%end': |
---|
1400 | break |
---|
1401 | log.warning(self._name_line_msg("unexpected '%s'" % (r[1]))) |
---|
1402 | elif r[0] == 'directive': |
---|
1403 | if r[1] == '%include': |
---|
1404 | self.load(r[2][0]) |
---|
1405 | continue |
---|
1406 | dir, info, data = self._process_directive(r, dir, info, data) |
---|
1407 | elif r[0] == 'data': |
---|
1408 | dir, info, data = self._process_data(r, dir, info, data) |
---|
1409 | else: |
---|
1410 | self._error("%d: invalid parse state: '%s" % (self.lc, r[0])) |
---|
1411 | if dir is not None: |
---|
1412 | self._directive_extend(dir, data) |
---|
1413 | except: |
---|
1414 | config.close() |
---|
1415 | raise |
---|
1416 | |
---|
1417 | config.close() |
---|
1418 | |
---|
1419 | self.name = save_name |
---|
1420 | self.lc = save_lc |
---|
1421 | |
---|
1422 | self.load_depth -= 1 |
---|
1423 | |
---|
1424 | def defined(self, name): |
---|
1425 | return name in self.macros |
---|
1426 | |
---|
1427 | def define(self, name): |
---|
1428 | if name in self.macros: |
---|
1429 | d = self.macros[name] |
---|
1430 | else: |
---|
1431 | n = self._label(name) |
---|
1432 | if n in self.macros: |
---|
1433 | d = self.macros[n] |
---|
1434 | else: |
---|
1435 | raise error.general('%d: macro "%s" not found' % (self.lc, name)) |
---|
1436 | return self._expand(d) |
---|
1437 | |
---|
1438 | def set_define(self, name, value): |
---|
1439 | self.macros[name] = value |
---|
1440 | |
---|
1441 | def expand(self, line): |
---|
1442 | if type(line) == list: |
---|
1443 | el = [] |
---|
1444 | for l in line: |
---|
1445 | el += [self._expand(l)] |
---|
1446 | return el |
---|
1447 | return self._expand(line) |
---|
1448 | |
---|
1449 | def macro(self, name): |
---|
1450 | if name in self.macros: |
---|
1451 | return self.macros[name] |
---|
1452 | raise error.general('macro "%s" not found' % (name)) |
---|
1453 | |
---|
1454 | def directive(self, _package, name): |
---|
1455 | if _package not in self._packages: |
---|
1456 | raise error.general('package "' + _package + '" not found') |
---|
1457 | if name not in self._packages[_package].directives: |
---|
1458 | raise error.general('directive "' + name + \ |
---|
1459 | '" not found in package "' + _package + '"') |
---|
1460 | return self._packages[_package].directives[name] |
---|
1461 | |
---|
1462 | def abspath(self, rpath): |
---|
1463 | return path.abspath(self.define(rpath)) |
---|
1464 | |
---|
1465 | def packages(self): |
---|
1466 | return self._packages |
---|
1467 | |
---|
1468 | def includes(self): |
---|
1469 | return self._includes |
---|
1470 | |
---|
1471 | def file_name(self): |
---|
1472 | return self.name |
---|
1473 | |
---|
1474 | def run(): |
---|
1475 | import sys |
---|
1476 | try: |
---|
1477 | # |
---|
1478 | # Run where defaults.mc is located |
---|
1479 | # |
---|
1480 | opts = options.load(sys.argv, defaults = 'defaults.mc') |
---|
1481 | log.trace('config: count %d' % (len(opts.config_files()))) |
---|
1482 | for config_file in opts.config_files(): |
---|
1483 | s = open(config_file, opts) |
---|
1484 | print(s) |
---|
1485 | del s |
---|
1486 | except error.general as gerr: |
---|
1487 | print(gerr) |
---|
1488 | sys.exit(1) |
---|
1489 | except error.internal as ierr: |
---|
1490 | print(ierr) |
---|
1491 | sys.exit(1) |
---|
1492 | except KeyboardInterrupt: |
---|
1493 | log.notice('abort: user terminated') |
---|
1494 | sys.exit(1) |
---|
1495 | sys.exit(0) |
---|
1496 | |
---|
1497 | if __name__ == "__main__": |
---|
1498 | run() |
---|