1 | # |
---|
2 | # $Id$ |
---|
3 | # |
---|
4 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
5 | # Copyright 2010 Chris Johns (chrisj@rtems.org) |
---|
6 | # All rights reserved. |
---|
7 | # |
---|
8 | # This file is part of the RTEMS Tools package in 'rtems-tools'. |
---|
9 | # |
---|
10 | # RTEMS Tools is free software: you can redistribute it and/or modify |
---|
11 | # it under the terms of the GNU General Public License as published by |
---|
12 | # the Free Software Foundation, either version 3 of the License, or |
---|
13 | # (at your option) any later version. |
---|
14 | # |
---|
15 | # RTEMS Tools is distributed in the hope that it will be useful, |
---|
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 | # GNU General Public License for more details. |
---|
19 | # |
---|
20 | # You should have received a copy of the GNU General Public License |
---|
21 | # along with RTEMS Tools. If not, see <http://www.gnu.org/licenses/>. |
---|
22 | # |
---|
23 | |
---|
24 | # |
---|
25 | # This code is based on what ever doco about spec files I could find and |
---|
26 | # RTEMS project's spec files. It parses a spec file into Python data types |
---|
27 | # that can be used by other software modules. |
---|
28 | # |
---|
29 | |
---|
30 | import os |
---|
31 | import re |
---|
32 | import sys |
---|
33 | |
---|
34 | import defaults |
---|
35 | import error |
---|
36 | import execute |
---|
37 | import log |
---|
38 | |
---|
39 | debug = False |
---|
40 | |
---|
41 | class package: |
---|
42 | |
---|
43 | def __init__(self, name, arch): |
---|
44 | self._name = name |
---|
45 | self._arch = arch |
---|
46 | self.directives = {} |
---|
47 | self.infos = {} |
---|
48 | |
---|
49 | def __str__(self): |
---|
50 | |
---|
51 | def _dictlist(dl): |
---|
52 | s = '' |
---|
53 | dll = dl.keys() |
---|
54 | dll.sort() |
---|
55 | for d in dll: |
---|
56 | s += ' ' + d + ':\n' |
---|
57 | for l in dl[d]: |
---|
58 | s += ' ' + l + '\n' |
---|
59 | return s |
---|
60 | |
---|
61 | s = '\npackage: ' + self._name + \ |
---|
62 | '\n directives:\n' + _dictlist(self.directives) + \ |
---|
63 | '\n infos:\n' + _dictlist(self.infos) |
---|
64 | |
---|
65 | return s |
---|
66 | |
---|
67 | def get_info(self, info): |
---|
68 | if not info in self.infos: |
---|
69 | raise error.general('no ' + info + ' in package "' + self.name + '"') |
---|
70 | return self.info |
---|
71 | |
---|
72 | def version(self): |
---|
73 | return self.get_info('Version') |
---|
74 | |
---|
75 | def extract_info(self, label): |
---|
76 | infos = {} |
---|
77 | for i in self.infos: |
---|
78 | il = i.lower() |
---|
79 | if il.startswith(label): |
---|
80 | if il == label: |
---|
81 | il = label + '0' |
---|
82 | elif not il[len(label):].isdigit(): |
---|
83 | continue |
---|
84 | infos[il] = self.infos[i] |
---|
85 | return infos |
---|
86 | |
---|
87 | def find_info(self, label): |
---|
88 | for i in self.infos: |
---|
89 | if i.lower() == label: |
---|
90 | return self.infos[i] |
---|
91 | return None |
---|
92 | |
---|
93 | def find_directive(self, label): |
---|
94 | for d in self.directives: |
---|
95 | if d.lower() == label: |
---|
96 | return self.directives[d] |
---|
97 | return None |
---|
98 | |
---|
99 | def name(self): |
---|
100 | info = self.find_info('name') |
---|
101 | if info: |
---|
102 | return info[0] |
---|
103 | return self._name |
---|
104 | |
---|
105 | def version(self): |
---|
106 | info = self.find_info('version') |
---|
107 | if not info: |
---|
108 | return None |
---|
109 | return info[0] |
---|
110 | |
---|
111 | def release(self): |
---|
112 | info = self.find_info('release') |
---|
113 | if not info: |
---|
114 | return None |
---|
115 | return info[0] |
---|
116 | |
---|
117 | def buildarch(self): |
---|
118 | info = self.find_info('buildarch') |
---|
119 | if not info: |
---|
120 | return self._arch |
---|
121 | return info[0] |
---|
122 | |
---|
123 | def sources(self): |
---|
124 | return self.extract_info('source'); |
---|
125 | |
---|
126 | def patches(self): |
---|
127 | return self.extract_info('patch') |
---|
128 | |
---|
129 | def prep(self): |
---|
130 | return self.find_directive('%prep') |
---|
131 | |
---|
132 | def build(self): |
---|
133 | return self.find_directive('%build') |
---|
134 | |
---|
135 | def install(self): |
---|
136 | return self.find_directive('%install') |
---|
137 | |
---|
138 | def clean(self): |
---|
139 | return self.find_directive('%clean') |
---|
140 | |
---|
141 | def post(self): |
---|
142 | return self.find_directive('%post') |
---|
143 | |
---|
144 | def long_name(self): |
---|
145 | buildarch = self.buildarch() |
---|
146 | return '-'.join([self.name(), self.version(), self.release()]) + \ |
---|
147 | '.' + buildarch |
---|
148 | |
---|
149 | class file: |
---|
150 | """Parse a spec file.""" |
---|
151 | |
---|
152 | _directive = [ '%description', |
---|
153 | '%changelog', |
---|
154 | '%prep', |
---|
155 | '%build', |
---|
156 | '%check', |
---|
157 | '%install', |
---|
158 | '%clean', |
---|
159 | '%post', |
---|
160 | '%preun', |
---|
161 | '%files' ] |
---|
162 | |
---|
163 | _ignore = [ re.compile('%setup'), |
---|
164 | re.compile('%configure'), |
---|
165 | re.compile('%doc'), |
---|
166 | re.compile('%dir'), |
---|
167 | re.compile('%ghost'), |
---|
168 | re.compile('%exclude'), |
---|
169 | re.compile('%source[0-9]*'), |
---|
170 | re.compile('%patch[0-9]*'), |
---|
171 | re.compile('%__os_install_post') ] |
---|
172 | |
---|
173 | def __init__(self, name, _defaults, opts): |
---|
174 | self.opts = opts |
---|
175 | self.specpath = 'not set' |
---|
176 | self.wss = re.compile(r'\s+') |
---|
177 | self.tags = re.compile(r':+') |
---|
178 | self.sf = re.compile(r'%\([^\)]+\)') |
---|
179 | self.default_defines = {} |
---|
180 | for d in _defaults: |
---|
181 | self.default_defines[self._label(d)] = _defaults[d] |
---|
182 | for arg in self.opts.args: |
---|
183 | if arg.startswith('--with-') or arg.startswith('--without-'): |
---|
184 | label = arg[2:].lower().replace('-', '_') |
---|
185 | self.default_defines[self._label(label)] = label |
---|
186 | self.load(name) |
---|
187 | |
---|
188 | def __str__(self): |
---|
189 | |
---|
190 | def _dict(dd): |
---|
191 | s = '' |
---|
192 | ddl = dd.keys() |
---|
193 | ddl.sort() |
---|
194 | for d in ddl: |
---|
195 | s += ' ' + d + ': ' + dd[d] + '\n' |
---|
196 | return s |
---|
197 | |
---|
198 | s = 'spec: %s' % (self.specpath) + \ |
---|
199 | '\n' + str(self.opts) + \ |
---|
200 | '\nlines parsed: %d' % (self.lc) + \ |
---|
201 | '\nname: ' + self.name + \ |
---|
202 | '\ndefines:\n' + _dict(self.defines) |
---|
203 | for _package in self._packages: |
---|
204 | s += str(self._packages[_package]) |
---|
205 | return s |
---|
206 | |
---|
207 | def _output(self, text): |
---|
208 | if not self.opts.quiet(): |
---|
209 | log.output(text) |
---|
210 | |
---|
211 | def _warning(self, msg): |
---|
212 | self._output('warning: ' + self.name + ':' + str(self.lc) + ': ' + msg) |
---|
213 | |
---|
214 | def _error(self, msg): |
---|
215 | print >> sys.stderr, \ |
---|
216 | 'error: ' + self.name + ':' + str(self.lc) + ': ' + msg |
---|
217 | self.in_error = True |
---|
218 | |
---|
219 | def _label(self, name): |
---|
220 | return '%{' + name.lower() + '}' |
---|
221 | |
---|
222 | def _macro_split(self, s): |
---|
223 | '''Split the string (s) up by macros. Only split on the |
---|
224 | outter level. Nested levels will need to split with futher calls.''' |
---|
225 | trace_me = False |
---|
226 | macros = [] |
---|
227 | nesting = [] |
---|
228 | has_braces = False |
---|
229 | c = 0 |
---|
230 | while c < len(s): |
---|
231 | if trace_me: |
---|
232 | print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting |
---|
233 | # |
---|
234 | # We need to watch for shell type variables or the form '${var}' because |
---|
235 | # they can upset the brace matching. |
---|
236 | # |
---|
237 | if s[c] == '%' or s[c] == '$': |
---|
238 | start = s[c] |
---|
239 | c += 1 |
---|
240 | if c == len(s): |
---|
241 | continue |
---|
242 | # |
---|
243 | # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ? |
---|
244 | # |
---|
245 | if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'): |
---|
246 | continue |
---|
247 | elif not s[c].isspace(): |
---|
248 | # |
---|
249 | # If this is a shell macro and we are at the outter |
---|
250 | # level or is '$var' forget it and move on. |
---|
251 | # |
---|
252 | if start == '$' and (s[c] != '{' or len(nesting) == 0): |
---|
253 | continue |
---|
254 | if s[c] == '{': |
---|
255 | this_has_braces = True |
---|
256 | else: |
---|
257 | this_has_braces = False |
---|
258 | nesting.append((c - 1, has_braces)) |
---|
259 | has_braces = this_has_braces |
---|
260 | elif len(nesting) > 0: |
---|
261 | if s[c] == '}' or (s[c].isspace() and not has_braces): |
---|
262 | # |
---|
263 | # Can have '%{?test: something %more}' where the |
---|
264 | # nested %more ends with the '}' which also ends |
---|
265 | # the outter macro. |
---|
266 | # |
---|
267 | if not has_braces: |
---|
268 | if s[c] == '}': |
---|
269 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
270 | nesting = nesting[:-1] |
---|
271 | if len(nesting) == 0: |
---|
272 | macros.append(s[macro_start:c].strip()) |
---|
273 | if len(nesting) > 0: |
---|
274 | macro_start, has_braces = nesting[len(nesting) - 1] |
---|
275 | nesting = nesting[:-1] |
---|
276 | if len(nesting) == 0: |
---|
277 | macros.append(s[macro_start:c + 1].strip()) |
---|
278 | c += 1 |
---|
279 | if trace_me: |
---|
280 | print 'ms:', macros |
---|
281 | return macros |
---|
282 | |
---|
283 | def _shell(self, line): |
---|
284 | sl = self.sf.findall(line) |
---|
285 | if len(sl): |
---|
286 | e = execute.capture_execution() |
---|
287 | for s in sl: |
---|
288 | exit_code, proc, output = e.shell(s[2:-1]) |
---|
289 | if exit_code == 0: |
---|
290 | line = line.replace(s, output) |
---|
291 | else: |
---|
292 | raise error.general('shell macro failed: ' + s + ': ' + output) |
---|
293 | return line |
---|
294 | |
---|
295 | def _expand(self, s): |
---|
296 | expanded = True |
---|
297 | while expanded: |
---|
298 | expanded = False |
---|
299 | ms = self._macro_split(s) |
---|
300 | for m in ms: |
---|
301 | mn = m |
---|
302 | # |
---|
303 | # A macro can be '%{macro}' or '%macro'. Turn the later into |
---|
304 | # the former. |
---|
305 | # |
---|
306 | show_warning = True |
---|
307 | if mn[1] != '{': |
---|
308 | for r in self._ignore: |
---|
309 | if r.match(mn) is not None: |
---|
310 | mn = None |
---|
311 | break |
---|
312 | else: |
---|
313 | mn = self._label(mn[1:]) |
---|
314 | show_warning = False |
---|
315 | elif m.startswith('%{expand'): |
---|
316 | colon = m.find(':') |
---|
317 | if colon < 8: |
---|
318 | self._warning('malformed expand macro, no colon found') |
---|
319 | else: |
---|
320 | e = self._expand(m[colon + 1:-1].strip()) |
---|
321 | s = s.replace(m, e) |
---|
322 | expanded = True |
---|
323 | mn = None |
---|
324 | elif m.startswith('%{with '): |
---|
325 | # |
---|
326 | # Change the ' ' to '_' because the macros have no spaces. |
---|
327 | # |
---|
328 | n = self._label('with_' + m[7:-1].strip()) |
---|
329 | if n in self.defines: |
---|
330 | s = s.replace(m, '1') |
---|
331 | else: |
---|
332 | s = s.replace(m, '0') |
---|
333 | expanded = True |
---|
334 | mn = None |
---|
335 | elif m.startswith('%{echo'): |
---|
336 | mn = None |
---|
337 | elif m.startswith('%{defined'): |
---|
338 | n = self._label(m[9:-1].strip()) |
---|
339 | if n in self.defines: |
---|
340 | s = s.replace(m, '1') |
---|
341 | else: |
---|
342 | s = s.replace(m, '0') |
---|
343 | expanded = True |
---|
344 | mn = None |
---|
345 | elif m.startswith('%{?') or m.startswith('%{!?'): |
---|
346 | if m[2] == '!': |
---|
347 | start = 4 |
---|
348 | else: |
---|
349 | start = 3 |
---|
350 | colon = m[start:].find(':') |
---|
351 | if colon < 0: |
---|
352 | if not m.endswith('}'): |
---|
353 | self._warning("malform conditional macro'" + m) |
---|
354 | mn = None |
---|
355 | else: |
---|
356 | mn = self._label(m[start:-1]) |
---|
357 | else: |
---|
358 | mn = self._label(m[start:start + colon]) |
---|
359 | if mn: |
---|
360 | if m.startswith('%{?'): |
---|
361 | if mn in self.defines: |
---|
362 | if colon >= 0: |
---|
363 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
364 | expanded = True |
---|
365 | mn = None |
---|
366 | else: |
---|
367 | mn = '%{nil}' |
---|
368 | else: |
---|
369 | if mn not in self.defines: |
---|
370 | if colon >= 0: |
---|
371 | s = s.replace(m, m[start + colon + 1:-1]) |
---|
372 | expanded = True |
---|
373 | mn = None |
---|
374 | else: |
---|
375 | mn = '%{nil}' |
---|
376 | if mn: |
---|
377 | if mn.lower() in self.defines: |
---|
378 | s = s.replace(m, self.defines[mn.lower()]) |
---|
379 | expanded = True |
---|
380 | elif show_warning: |
---|
381 | self._error("macro '" + mn + "' not found") |
---|
382 | return self._shell(s) |
---|
383 | |
---|
384 | def _define(self, spec, ls): |
---|
385 | if len(ls) <= 1: |
---|
386 | self._warning('invalid macro definition') |
---|
387 | else: |
---|
388 | d = self._label(ls[1]) |
---|
389 | if d not in self.defines: |
---|
390 | if len(ls) == 2: |
---|
391 | self.defines[d] = '1' |
---|
392 | else: |
---|
393 | self.defines[d] = ls[2].strip() |
---|
394 | else: |
---|
395 | if self.opts.warn_all(): |
---|
396 | self._warning("macro '" + d + "' already defined") |
---|
397 | |
---|
398 | def _undefine(self, spec, ls): |
---|
399 | if len(ls) <= 1: |
---|
400 | self._warning('invalid macro definition') |
---|
401 | else: |
---|
402 | mn = self._label(ls[1]) |
---|
403 | if mn in self.defines: |
---|
404 | self._error("macro '" + mn + "' not defined") |
---|
405 | del self.defines[mn] |
---|
406 | |
---|
407 | def _ifs(self, spec, ls, label, iftrue, isvalid): |
---|
408 | text = [] |
---|
409 | in_iftrue = True |
---|
410 | while True: |
---|
411 | if isvalid and \ |
---|
412 | ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)): |
---|
413 | this_isvalid = True |
---|
414 | else: |
---|
415 | this_isvalid = False |
---|
416 | r = self._parse(spec, roc = True, isvalid = this_isvalid) |
---|
417 | if r[0] == 'control': |
---|
418 | if r[1] == '%end': |
---|
419 | self._error(label + ' without %endif') |
---|
420 | if r[1] == '%endif': |
---|
421 | return text |
---|
422 | if r[1] == '%else': |
---|
423 | in_iftrue = False |
---|
424 | elif r[0] == 'data': |
---|
425 | if this_isvalid: |
---|
426 | text.extend(r[1]) |
---|
427 | |
---|
428 | def _if(self, spec, ls, isvalid): |
---|
429 | |
---|
430 | global debug |
---|
431 | |
---|
432 | def add(x, y): |
---|
433 | return x + ' ' + str(y) |
---|
434 | |
---|
435 | def check_bool(value): |
---|
436 | if value.isdigit(): |
---|
437 | if int(value) == 0: |
---|
438 | istrue = False |
---|
439 | else: |
---|
440 | istrue = True |
---|
441 | else: |
---|
442 | istrue = None |
---|
443 | return istrue |
---|
444 | |
---|
445 | istrue = False |
---|
446 | if isvalid: |
---|
447 | if len(ls) == 2: |
---|
448 | s = ls[1] |
---|
449 | else: |
---|
450 | s = (ls[1] + ' ' + ls[2]) |
---|
451 | ifls = s.split() |
---|
452 | if len(ifls) == 1: |
---|
453 | istrue = check_bool(ifls[0]) |
---|
454 | if istrue == None: |
---|
455 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
456 | istrue = False |
---|
457 | elif len(ifls) == 2: |
---|
458 | if ifls[0] == '!': |
---|
459 | istrue = check_bool(ifls[1]) |
---|
460 | if istrue == None: |
---|
461 | self._error('invalid if bool value: ' + reduce(add, ls, '')) |
---|
462 | istrue = False |
---|
463 | else: |
---|
464 | istrue = not istrue |
---|
465 | else: |
---|
466 | self._error('invalid if bool operator: ' + reduce(add, ls, '')) |
---|
467 | elif len(ifls) == 3: |
---|
468 | if ifls[1] == '==': |
---|
469 | if ifls[0] == ifls[2]: |
---|
470 | istrue = True |
---|
471 | else: |
---|
472 | istrue = False |
---|
473 | elif ifls[1] == '!=' or ifls[1] == '=!': |
---|
474 | if ifls[0] != ifls[2]: |
---|
475 | istrue = True |
---|
476 | else: |
---|
477 | istrue = False |
---|
478 | elif ifls[1] == '>': |
---|
479 | if ifls[0] > ifls[2]: |
---|
480 | istrue = True |
---|
481 | else: |
---|
482 | istrue = False |
---|
483 | elif ifls[1] == '>=' or ifls[1] == '=>': |
---|
484 | if ifls[0] >= ifls[2]: |
---|
485 | istrue = True |
---|
486 | else: |
---|
487 | istrue = False |
---|
488 | elif ifls[1] == '<=' or ifls[1] == '=<': |
---|
489 | if ifls[0] <= ifls[2]: |
---|
490 | istrue = True |
---|
491 | else: |
---|
492 | istrue = False |
---|
493 | elif ifls[1] == '<': |
---|
494 | if ifls[0] < ifls[2]: |
---|
495 | istrue = True |
---|
496 | else: |
---|
497 | istrue = False |
---|
498 | else: |
---|
499 | self._error('invalid %if operator: ' + reduce(add, ls, '')) |
---|
500 | else: |
---|
501 | self._error('malformed if: ' + reduce(add, ls, '')) |
---|
502 | if debug: |
---|
503 | print '_if: ', ifls, istrue |
---|
504 | return self._ifs(spec, ls, '%if', istrue, isvalid) |
---|
505 | |
---|
506 | def _ifos(self, spec, ls, isvalid): |
---|
507 | isos = False |
---|
508 | if isvalid: |
---|
509 | os = self.define('_os') |
---|
510 | if ls[0].find(os) >= 0 or ls[1].find(os) >= 0: |
---|
511 | isos = True |
---|
512 | else: |
---|
513 | isos = False |
---|
514 | return self._ifs(spec, ls, '%ifos', isos, isvalid) |
---|
515 | |
---|
516 | def _ifarch(self, spec, positive, ls, isvalid): |
---|
517 | isarch = False |
---|
518 | if isvalid: |
---|
519 | arch = self.define('_arch') |
---|
520 | if ls[0].find(arch) >= 0 or ls[1].find(arch) >= 0: |
---|
521 | isarch = True |
---|
522 | else: |
---|
523 | isarch = False |
---|
524 | if not positive: |
---|
525 | isarch = not isarch |
---|
526 | return self._ifs(spec, ls, '%ifarch', isarch, isvalid) |
---|
527 | |
---|
528 | def _parse(self, spec, roc = False, isvalid = True): |
---|
529 | # roc = return on control |
---|
530 | |
---|
531 | global debug |
---|
532 | |
---|
533 | def _clean(line): |
---|
534 | line = line[0:-1] |
---|
535 | b = line.find('#') |
---|
536 | if b >= 0: |
---|
537 | line = line[1:b] |
---|
538 | return line.strip() |
---|
539 | |
---|
540 | # |
---|
541 | # Need to add code to count matching '{' and '}' and if they |
---|
542 | # do not match get the next line and add to the string until |
---|
543 | # they match. This closes an opening '{' that is on another |
---|
544 | # line. |
---|
545 | # |
---|
546 | |
---|
547 | for l in spec: |
---|
548 | self.lc += 1 |
---|
549 | l = _clean(l) |
---|
550 | if len(l) == 0: |
---|
551 | continue |
---|
552 | if debug: |
---|
553 | print '%03d: %d %s' % (self.lc, isvalid, l) |
---|
554 | if isvalid: |
---|
555 | l = self._expand(l) |
---|
556 | if len(l) == 0: |
---|
557 | continue |
---|
558 | if l[0] == '%': |
---|
559 | ls = self.wss.split(l, 2) |
---|
560 | if ls[0] == '%package': |
---|
561 | if isvalid: |
---|
562 | if ls[1] == '-n': |
---|
563 | name = ls[2] |
---|
564 | else: |
---|
565 | name = self.name + '-' + ls[1] |
---|
566 | return ('package', name) |
---|
567 | elif ls[0] == '%define' or ls[0] == '%global': |
---|
568 | if isvalid: |
---|
569 | self._define(spec, ls) |
---|
570 | elif ls[0] == '%undefine': |
---|
571 | if isvalid: |
---|
572 | self._undefine(spec, ls) |
---|
573 | elif ls[0] == '%if': |
---|
574 | d = self._if(spec, ls, isvalid) |
---|
575 | if len(d): |
---|
576 | return ('data', d) |
---|
577 | elif ls[0] == '%ifos': |
---|
578 | d = self._ifos(spec, ls, isvalid) |
---|
579 | if len(d): |
---|
580 | return ('data', d) |
---|
581 | elif ls[0] == '%ifarch': |
---|
582 | d = self._ifarch(spec, True, ls, isvalid) |
---|
583 | if len(d): |
---|
584 | return ('data', d) |
---|
585 | elif ls[0] == '%ifnarch': |
---|
586 | d = self._ifarch(spec, False, ls, isvalid) |
---|
587 | if len(d): |
---|
588 | return ('data', d) |
---|
589 | elif ls[0] == '%endif': |
---|
590 | if roc: |
---|
591 | return ('control', '%endif') |
---|
592 | self._warning("unexpected '" + ls[0] + "'") |
---|
593 | elif ls[0] == '%else': |
---|
594 | if roc: |
---|
595 | return ('control', '%else') |
---|
596 | self._warning("unexpected '" + ls[0] + "'") |
---|
597 | elif ls[0].startswith('%defattr'): |
---|
598 | return ('data', [l]) |
---|
599 | elif ls[0] == '%bcond_with': |
---|
600 | if isvalid: |
---|
601 | # |
---|
602 | # Check is already defined. Would be by the command line or |
---|
603 | # even a host specific default. |
---|
604 | # |
---|
605 | if self._label('with_' + ls[1]) not in self.defines: |
---|
606 | self._define(spec, (ls[0], 'without_' + ls[1])) |
---|
607 | elif ls[0] == '%bcond_without': |
---|
608 | if isvalid: |
---|
609 | if self._label('without_' + ls[1]) not in self.defines: |
---|
610 | self._define(spec, (ls[0], 'with_' + ls[1])) |
---|
611 | else: |
---|
612 | for r in self._ignore: |
---|
613 | if r.match(ls[0]) is not None: |
---|
614 | return ('data', [l]) |
---|
615 | if isvalid: |
---|
616 | for d in self._directive: |
---|
617 | if ls[0].strip() == d: |
---|
618 | return ('directive', ls[0].strip(), ls[1:]) |
---|
619 | self._warning("unknown directive: '" + ls[0] + "'") |
---|
620 | return ('data', [l]) |
---|
621 | else: |
---|
622 | return ('data', [l]) |
---|
623 | return ('control', '%end') |
---|
624 | |
---|
625 | def _set_package(self, _package): |
---|
626 | if self.package == 'main' and \ |
---|
627 | self._packages[self.package].name() != None: |
---|
628 | if self._packages[self.package].name() == _package: |
---|
629 | return |
---|
630 | if _package not in self._packages: |
---|
631 | self._packages[_package] = package(_package, |
---|
632 | self.define('%{_arch}')) |
---|
633 | self.package = _package |
---|
634 | |
---|
635 | def _directive_extend(self, dir, data): |
---|
636 | if dir not in self._packages[self.package].directives: |
---|
637 | self._packages[self.package].directives[dir] = [] |
---|
638 | for i in range(0, len(data)): |
---|
639 | data[i] = data[i].strip() |
---|
640 | self._packages[self.package].directives[dir].extend(data) |
---|
641 | |
---|
642 | def _info_append(self, info, data): |
---|
643 | if info not in self._packages[self.package].infos: |
---|
644 | self._packages[self.package].infos[info] = [] |
---|
645 | self._packages[self.package].infos[info].append(data) |
---|
646 | |
---|
647 | def load(self, name): |
---|
648 | |
---|
649 | global debug |
---|
650 | |
---|
651 | self.in_error = False |
---|
652 | self.name = name |
---|
653 | self.lc = 0 |
---|
654 | self.defines = self.default_defines |
---|
655 | self.conditionals = {} |
---|
656 | self._packages = {} |
---|
657 | self.package = 'main' |
---|
658 | self._packages[self.package] = package(self.package, |
---|
659 | self.define('%{_arch}')) |
---|
660 | self.specpath = os.path.join(self.abspath('_specdir'), name) |
---|
661 | if not os.path.exists(self.specpath): |
---|
662 | raise error.general('no spec file found: ' + self.specpath) |
---|
663 | try: |
---|
664 | spec = open(self.specpath, 'r') |
---|
665 | except IOError, err: |
---|
666 | raise error.general('error opening spec file: ' + self.specpath) |
---|
667 | try: |
---|
668 | dir = None |
---|
669 | data = [] |
---|
670 | while True: |
---|
671 | r = self._parse(spec) |
---|
672 | if r[0] == 'package': |
---|
673 | self._set_package(r[1]) |
---|
674 | dir = None |
---|
675 | elif r[0] == 'control': |
---|
676 | if r[1] == '%end': |
---|
677 | break |
---|
678 | self._warning("unexpected '" + r[1] + "'") |
---|
679 | elif r[0] == 'directive': |
---|
680 | new_data = [] |
---|
681 | if r[1] == '%description': |
---|
682 | new_data = [' '.join(r[2])] |
---|
683 | else: |
---|
684 | if len(r[2]) == 0: |
---|
685 | _package = 'main' |
---|
686 | elif len(r[2]) == 1: |
---|
687 | _package = r[2][0] |
---|
688 | else: |
---|
689 | if r[2][0].strip() != '-n': |
---|
690 | self._warning("unknown directive option: '" + ' '.join(r[2]) + "'") |
---|
691 | _package = r[2][1].strip() |
---|
692 | self._set_package(_package) |
---|
693 | if dir and dir != r[1]: |
---|
694 | self._directive_extend(dir, data) |
---|
695 | dir = r[1] |
---|
696 | data = new_data |
---|
697 | elif r[0] == 'data': |
---|
698 | for l in r[1]: |
---|
699 | l = self._expand(l) |
---|
700 | if not dir: |
---|
701 | ls = self.tags.split(l, 1) |
---|
702 | if debug: |
---|
703 | print '_tag: ', l, ls |
---|
704 | if len(ls) > 1: |
---|
705 | i = ls[0] |
---|
706 | self._info_append(i, ls[1].strip()) |
---|
707 | # It seems like the info's also appear as |
---|
708 | # defines or can be accessed via macros. |
---|
709 | if ls[0][len(ls[0]) - 1] == ':': |
---|
710 | ls[0] = ls[0][:-1] |
---|
711 | ls[0] = ls[0].lower() |
---|
712 | self._define(None, ('', ls[0], ls[1])) |
---|
713 | else: |
---|
714 | self._warning("invalid format: '" + l[:-1] + "'") |
---|
715 | else: |
---|
716 | data.append(l) |
---|
717 | else: |
---|
718 | self._error("invalid parse state: '" + r[0] + "'") |
---|
719 | self._directive_extend(dir, data) |
---|
720 | except: |
---|
721 | spec.close() |
---|
722 | raise |
---|
723 | spec.close() |
---|
724 | |
---|
725 | def define(self, name): |
---|
726 | if name.lower() in self.defines: |
---|
727 | d = self.defines[name.lower()] |
---|
728 | else: |
---|
729 | n = self._label(name) |
---|
730 | if n in self.defines: |
---|
731 | d = self.defines[n] |
---|
732 | else: |
---|
733 | raise error.general('macro "' + name + '" not found') |
---|
734 | return self._expand(d) |
---|
735 | |
---|
736 | def expand(self, line): |
---|
737 | return self._expand(line) |
---|
738 | |
---|
739 | def directive(self, _package, name): |
---|
740 | if _package not in self._packages: |
---|
741 | raise error.general('package "' + _package + '" not found') |
---|
742 | if name not in self._packages[_package].directives: |
---|
743 | raise error.general('directive "' + name + \ |
---|
744 | '" not found in package "' + _package + '"') |
---|
745 | return self._packages[_package].directives[name] |
---|
746 | |
---|
747 | def abspath(self, path): |
---|
748 | return os.path.abspath(self.define(path)) |
---|
749 | |
---|
750 | def packages(self): |
---|
751 | return self._packages |
---|
752 | |
---|
753 | def run(): |
---|
754 | import sys |
---|
755 | try: |
---|
756 | opts, _defaults = defaults.load(sys.argv) |
---|
757 | for spec_file in opts.spec_files(): |
---|
758 | s = file(spec_file, _defaults = _defaults, opts = opts) |
---|
759 | print s |
---|
760 | del s |
---|
761 | except error.general, gerr: |
---|
762 | print gerr |
---|
763 | sys.exit(1) |
---|
764 | except error.internal, ierr: |
---|
765 | print ierr |
---|
766 | sys.exit(1) |
---|
767 | except KeyboardInterrupt: |
---|
768 | print 'user terminated' |
---|
769 | sys.exit(1) |
---|
770 | sys.exit(0) |
---|
771 | |
---|
772 | if __name__ == "__main__": |
---|
773 | run() |
---|