source: rtems-source-builder/source-builder/sb/config.py @ 517f036

4.104.114.9
Last change on this file since 517f036 was 517f036, checked in by Chris Johns <chrisj@…>, on Apr 27, 2013 at 10:26:49 AM

Add '%disable select' to turn off the macros maps.

  • Property mode set to 100644
File size: 35.3 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2013 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
28import copy
29import os
30import re
31import sys
32
33try:
34    import error
35    import execute
36    import log
37    import options
38    import path
39except KeyboardInterrupt:
40    print 'user terminated'
41    sys.exit(1)
42except:
43    print 'error: unknown application load error'
44    sys.exit(1)
45
46def _check_bool(value):
47    if value.isdigit():
48        if int(value) == 0:
49            istrue = False
50        else:
51            istrue = True
52    else:
53        istrue = None
54    return istrue
55
56class package:
57
58    def __init__(self, name, arch, config):
59        self._name = name
60        self._arch = arch
61        self.config = config
62        self.directives = {}
63        self.infos = {}
64
65    def __str__(self):
66
67        def _dictlist(dl):
68            s = ''
69            dll = dl.keys()
70            dll.sort()
71            for d in dll:
72                if d:
73                    s += '  ' + d + ':\n'
74                    for l in dl[d]:
75                        s += '    ' + l + '\n'
76            return s
77
78        s = '\npackage: ' + self._name + \
79            '\n directives:\n' + _dictlist(self.directives) + \
80            '\n infos:\n' + _dictlist(self.infos)
81
82        return s
83
84    def _macro_override(self, info, macro):
85        '''See if a macro overrides this setting.'''
86        overridden = self.config.macros.overridden(macro)
87        if overridden:
88            return self.config.macros.expand(macro)
89        return info
90
91    def directive_extend(self, dir, data):
92        if dir not in self.directives:
93            self.directives[dir] = []
94        for i in range(0, len(data)):
95            data[i] = data[i].strip()
96        self.directives[dir].extend(data)
97        self.config.macros[dir] = '\n'.join(self.directives[dir])
98
99    def info_append(self, info, data):
100        if info not in self.infos:
101            self.infos[info] = []
102        self.infos[info].append(data)
103        self.config.macros[info] = '\n'.join(self.infos[info])
104
105    def get_info(self, info, expand = True):
106        if info in self.config.macros:
107            _info = self.config.macros[info].split('\n')
108            if expand:
109                return self.config.expand(_info)
110            else:
111                return _info
112        return None
113
114    def extract_info(self, label, expand = True):
115        ll = label.lower()
116        infos = {}
117        keys = self.config.macros.find('%s.*' % (ll))
118        for k in keys:
119            if k == ll:
120                k = '%s0' % (ll)
121            elif not k[len(ll):].isdigit():
122                continue
123            infos[k] = [self.config.expand(self.config.macros[k])]
124        return infos
125
126    def _find_macro(self, label, expand = True):
127        if label in self.config.macros:
128            macro = self.config.macros[label].split('\n')
129            if expand:
130                return self.config.expand(macro)
131            else:
132                return macro
133        return None
134
135    def find_info(self, label, expand = True):
136        return self._find_macro(label, expand)
137
138    def find_directive(self, label, expand = True):
139        return self._find_macro(label, expand)
140
141    def name(self):
142        info = self.find_info('name')
143        if info:
144            n = info[0]
145        else:
146            n = self._name
147        return self._macro_override(n, 'name')
148
149    def summary(self):
150        info = self.find_info('summary')
151        if info:
152            return info[0]
153        return ''
154
155    def url(self):
156        info = self.find_info('url')
157        if info:
158            return info[0]
159        return ''
160
161    def version(self):
162        info = self.find_info('version')
163        if not info:
164            return None
165        return info[0]
166
167    def release(self):
168        info = self.find_info('release')
169        if not info:
170            return None
171        return info[0]
172
173    def buildarch(self):
174        info = self.find_info('buildarch')
175        if not info:
176            return self._arch
177        return info[0]
178
179    def sources(self):
180        return self.extract_info('source')
181
182    def patches(self):
183        return self.extract_info('patch')
184
185    def prep(self):
186        return self.find_directive('%prep')
187
188    def build(self):
189        return self.find_directive('%build')
190
191    def install(self):
192        return self.find_directive('%install')
193
194    def clean(self):
195        return self.find_directive('%clean')
196
197    def include(self):
198        return self.find_directive('%include')
199
200    def testing(self):
201        return self.find_directive('%testing')
202
203    def long_name(self):
204        return self.name()
205
206class file:
207    """Parse a config file."""
208
209    _directive = [ '%description',
210                   '%prep',
211                   '%build',
212                   '%clean',
213                   '%install',
214                   '%include',
215                   '%install',
216                   '%testing' ]
217
218    _ignore = [ re.compile('%setup'),
219                re.compile('%configure'),
220                re.compile('%source[0-9]*'),
221                re.compile('%patch[0-9]*'),
222                re.compile('%select'),
223                re.compile('%disable') ]
224
225    def __init__(self, name, opts, macros = None):
226        self.opts = opts
227        if macros is None:
228            self.macros = opts.defaults
229        else:
230            self.macros = macros
231        self.init_name = name
232        log.trace('config: %s' % (name))
233        self.disable_macro_reassign = False
234        self.configpath = []
235        self.wss = re.compile(r'\s+')
236        self.tags = re.compile(r':+')
237        self.sf = re.compile(r'%\([^\)]+\)')
238        for arg in self.opts.args:
239            if arg.startswith('--with-') or arg.startswith('--without-'):
240                label = arg[2:].lower().replace('-', '_')
241                self.macros.define(label)
242        self._includes = []
243        self.load_depth = 0
244        self.load(name)
245
246    def __str__(self):
247
248        def _dict(dd):
249            s = ''
250            ddl = dd.keys()
251            ddl.sort()
252            for d in ddl:
253                s += '  ' + d + ': ' + dd[d] + '\n'
254            return s
255
256        s = 'config: %s' % ('.'.join(self.configpath)) + \
257            '\n' + str(self.opts) + \
258            '\nlines parsed: %d' % (self.lc) + \
259            '\nname: ' + self.name + \
260            '\nmacros:\n' + str(self.macros)
261        for _package in self._packages:
262            s += str(self._packages[_package])
263        return s
264
265    def _name_line_msg(self,  msg):
266        return '%s:%d: %s' % (path.basename(self.init_name), self.lc,  msg)
267
268    def _output(self, text):
269        if not self.opts.quiet():
270            log.output(text)
271
272    def _error(self, msg):
273        err = 'error: %s' % (self._name_line_msg(msg))
274        log.stderr(err)
275        log.output(err)
276        self.in_error = True
277        if not self.opts.dry_run():
278            log.stderr('warning: switched to dry run due to errors')
279            self.opts.set_dry_run()
280
281    def _label(self, name):
282        if name.startswith('%{') and name[-1] is '}':
283            return name
284        return '%{' + name.lower() + '}'
285
286    def _macro_split(self, s):
287        '''Split the string (s) up by macros. Only split on the
288           outter level. Nested levels will need to split with futher calls.'''
289        trace_me = False
290        if trace_me:
291            print '------------------------------------------------------'
292        macros = []
293        nesting = []
294        has_braces = False
295        c = 0
296        while c < len(s):
297            if trace_me:
298                print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting
299            #
300            # We need to watch for shell type variables or the form '${var}' because
301            # they can upset the brace matching.
302            #
303            if s[c] == '%' or s[c] == '$':
304                start = s[c]
305                c += 1
306                if c == len(s):
307                    continue
308                #
309                # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ?
310                #
311                if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'):
312                    continue
313                elif not s[c].isspace():
314                    #
315                    # If this is a shell macro and we are at the outter
316                    # level or is '$var' forget it and move on.
317                    #
318                    if start == '$' and (s[c] != '{' or len(nesting) == 0):
319                        continue
320                    if s[c] == '{':
321                        this_has_braces = True
322                    else:
323                        this_has_braces = False
324                    nesting.append((c - 1, has_braces))
325                    has_braces = this_has_braces
326            elif len(nesting) > 0:
327                if s[c] == '}' or (s[c].isspace() and not has_braces):
328                    #
329                    # Can have '%{?test: something %more}' where the
330                    # nested %more ends with the '}' which also ends
331                    # the outter macro.
332                    #
333                    if not has_braces:
334                        if s[c] == '}':
335                            macro_start, has_braces = nesting[len(nesting) - 1]
336                            nesting = nesting[:-1]
337                            if len(nesting) == 0:
338                                macros.append(s[macro_start:c].strip())
339                    if len(nesting) > 0:
340                        macro_start, has_braces = nesting[len(nesting) - 1]
341                        nesting = nesting[:-1]
342                        if len(nesting) == 0:
343                            macros.append(s[macro_start:c + 1].strip())
344            c += 1
345        if trace_me:
346            print 'ms:', macros
347        if trace_me:
348            print '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='
349        return macros
350
351    def _shell(self, line):
352        sl = self.sf.findall(line)
353        if len(sl):
354            e = execute.capture_execution()
355            for s in sl:
356                exit_code, proc, output = e.shell(s[2:-1])
357                if exit_code == 0:
358                    line = line.replace(s, output)
359                else:
360                    raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output))
361        return line
362
363    def _expand(self, s):
364        expand_count = 0
365        expanded = True
366        while expanded:
367            expand_count += 1
368            if expand_count > 500:
369                raise error.general('macro expand looping: %s' % (s))
370            expanded = False
371            ms = self._macro_split(s)
372            for m in ms:
373                mn = m
374                #
375                # A macro can be '%{macro}' or '%macro'. Turn the later into
376                # the former.
377                #
378                show_warning = True
379                if mn[1] != '{':
380                    for r in self._ignore:
381                        if r.match(mn) is not None:
382                            mn = None
383                            break
384                    else:
385                        mn = self._label(mn[1:])
386                        show_warning = False
387                elif m.startswith('%{expand'):
388                    colon = m.find(':')
389                    if colon < 8:
390                        log.warning('malformed expand macro, no colon found')
391                    else:
392                        e = self._expand(m[colon + 1:-1].strip())
393                        s = s.replace(m, e)
394                        expanded = True
395                        mn = None
396                elif m.startswith('%{with '):
397                    #
398                    # Change the ' ' to '_' because the macros have no spaces.
399                    #
400                    n = self._label('with_' + m[7:-1].strip())
401                    if n in self.macros:
402                        s = s.replace(m, '1')
403                    else:
404                        s = s.replace(m, '0')
405                    expanded = True
406                    mn = None
407                elif m.startswith('%{echo'):
408                    if not m.endswith('}'):
409                        log.warning("malformed conditional macro '%s'" % (m))
410                        mn = None
411                    else:
412                        e = self._expand(m[6:-1].strip())
413                        log.output('%s' % (self._name_line_msg(e)))
414                        s = ''
415                        expanded = True
416                        mn = None
417                elif m.startswith('%{defined'):
418                    n = self._label(m[9:-1].strip())
419                    if n in self.macros:
420                        s = s.replace(m, '1')
421                    else:
422                        s = s.replace(m, '0')
423                    expanded = True
424                    mn = None
425                elif m.startswith('%{?') or m.startswith('%{!?'):
426                    if m[2] == '!':
427                        start = 4
428                    else:
429                        start = 3
430                    colon = m[start:].find(':')
431                    if colon < 0:
432                        if not m.endswith('}'):
433                            log.warning("malformed conditional macro '%s'" % (m))
434                            mn = None
435                        else:
436                            mn = self._label(m[start:-1])
437                    else:
438                        mn = self._label(m[start:start + colon])
439                    if mn:
440                        if m.startswith('%{?'):
441                            istrue = False
442                            if mn in self.macros:
443                                # If defined and 0 then it is false.
444                                istrue = _check_bool(self.macros[mn])
445                                if istrue is None:
446                                    istrue = True
447                            if colon >= 0 and istrue:
448                                s = s.replace(m, m[start + colon + 1:-1])
449                                expanded = True
450                                mn = None
451                            elif not istrue:
452                                mn = '%{nil}'
453                        else:
454                            isfalse = True
455                            if mn in self.macros:
456                                istrue = _check_bool(self.macros[mn])
457                                if istrue is None or istrue == True:
458                                    isfalse = False
459                            if colon >= 0 and isfalse:
460                                s = s.replace(m, m[start + colon + 1:-1])
461                                expanded = True
462                                mn = None
463                            else:
464                                mn = '%{nil}'
465                if mn:
466                    if mn.lower() in self.macros:
467                        s = s.replace(m, self.macros[mn.lower()])
468                        expanded = True
469                    elif show_warning:
470                        self._error("macro '%s' not found" % (mn))
471        return self._shell(s)
472
473    def _disable(self, config, ls):
474        if len(ls) != 2:
475            log.warning('invalid disable statement')
476        else:
477            if ls[1] == 'select':
478                self.macros.lock_read_map()
479                log.trace('config: %s: _disable_select: %s %s' % (self.init_name, r, ls[1]))
480            else:
481                log.warning('invalid disable statement: %s' % (ls[1]))
482
483    def _select(self, config, ls):
484        if len(ls) != 2:
485            log.warning('invalid select statement')
486        else:
487            r = self.macros.set_read_map(ls[1])
488            log.trace('config: %s: _select: %s %s %r' % \
489                          (self.init_name, r, ls[1], self.macros.maps()))
490
491    def _define(self, config, ls):
492        if len(ls) <= 1:
493            log.warning('invalid macro definition')
494        else:
495            d = self._label(ls[1])
496            if self.disable_macro_reassign:
497                if (d not in self.macros) or \
498                        (d in self.macros and len(self.macros[d]) == 0):
499                    if len(ls) == 2:
500                        self.macros[d] = '1'
501                    else:
502                        self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
503                else:
504                    log.warning("macro '%s' already defined" % (d))
505            else:
506                if len(ls) == 2:
507                    self.macros[d] = '1'
508                else:
509                    self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
510
511    def _undefine(self, config, ls):
512        if len(ls) <= 1:
513            log.warning('invalid macro definition')
514        else:
515            mn = self._label(ls[1])
516            if mn in self.macros:
517                del self.macros[mn]
518            else:
519                log.warning("macro '%s' not defined" % (mn))
520
521    def _ifs(self, config, ls, label, iftrue, isvalid):
522        text = []
523        in_iftrue = True
524        while True:
525            if isvalid and \
526                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
527                this_isvalid = True
528            else:
529                this_isvalid = False
530            r = self._parse(config, roc = True, isvalid = this_isvalid)
531            if r[0] == 'control':
532                if r[1] == '%end':
533                    self._error(label + ' without %endif')
534                    raise error.general('terminating build')
535                if r[1] == '%endif':
536                    return text
537                if r[1] == '%else':
538                    in_iftrue = False
539            elif r[0] == 'data':
540                if this_isvalid:
541                    text.extend(r[1])
542
543    def _if(self, config, ls, isvalid, invert = False):
544
545        def add(x, y):
546            return x + ' ' + str(y)
547
548        istrue = False
549        if isvalid:
550            if len(ls) == 2:
551                s = ls[1]
552            else:
553                s = (ls[1] + ' ' + ls[2])
554            ifls = s.split()
555            if len(ifls) == 1:
556                #
557                # Check if '%if %{x} == %{nil}' has both parts as nothing
558                # which means '%if ==' is always True and '%if !=' is always false.
559                #
560                if ifls[0] == '==':
561                    istrue = True
562                elif ifls[0] == '!=':
563                    istrue = False
564                else:
565                    istrue = _check_bool(ifls[0])
566                    if istrue == None:
567                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
568                        istrue = False
569            elif len(ifls) == 2:
570                if ifls[0] == '!':
571                    istrue = _check_bool(ifls[1])
572                    if istrue == None:
573                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
574                        istrue = False
575                    else:
576                        istrue = not istrue
577                else:
578                    #
579                    # Check is something is being checked against empty,
580                    #   ie '%if %{x} == %{nil}'
581                    # The logic is 'something == nothing' is False and
582                    # 'something != nothing' is True.
583                    #
584                    if ifls[1] == '==':
585                        istrue = False
586                    elif  ifls[1] == '!=':
587                        istrue = True
588                    else:
589                        self._error('invalid if bool operator: ' + reduce(add, ls, ''))
590            elif len(ifls) == 3:
591                if ifls[1] == '==':
592                    if ifls[0] == ifls[2]:
593                        istrue = True
594                    else:
595                        istrue = False
596                elif ifls[1] == '!=' or ifls[1] == '=!':
597                    if ifls[0] != ifls[2]:
598                        istrue = True
599                    else:
600                        istrue = False
601                elif ifls[1] == '>':
602                    if ifls[0] > ifls[2]:
603                        istrue = True
604                    else:
605                        istrue = False
606                elif ifls[1] == '>=' or ifls[1] == '=>':
607                    if ifls[0] >= ifls[2]:
608                        istrue = True
609                    else:
610                        istrue = False
611                elif ifls[1] == '<=' or ifls[1] == '=<':
612                    if ifls[0] <= ifls[2]:
613                        istrue = True
614                    else:
615                        istrue = False
616                elif ifls[1] == '<':
617                    if ifls[0] < ifls[2]:
618                        istrue = True
619                    else:
620                        istrue = False
621                else:
622                    self._error('invalid %if operator: ' + reduce(add, ls, ''))
623            else:
624                self._error('malformed if: ' + reduce(add, ls, ''))
625            if invert:
626                istrue = not istrue
627            log.trace('config: %s: _if:  %s %s' % (self.init_name, ifls, str(istrue)))
628        return self._ifs(config, ls, '%if', istrue, isvalid)
629
630    def _ifos(self, config, ls, isvalid):
631        isos = False
632        if isvalid:
633            os = self.define('_os')
634            for l in ls:
635                if l in os:
636                    isos = True
637                    break
638        return self._ifs(config, ls, '%ifos', isos, isvalid)
639
640    def _ifarch(self, config, positive, ls, isvalid):
641        isarch = False
642        if isvalid:
643            arch = self.define('_arch')
644            for l in ls:
645                if l in arch:
646                    isarch = True
647                    break
648        if not positive:
649            isarch = not isarch
650        return self._ifs(config, ls, '%ifarch', isarch, isvalid)
651
652    def _parse(self, config, roc = False, isvalid = True):
653        # roc = return on control
654
655        def _clean(line):
656            line = line[0:-1]
657            b = line.find('#')
658            if b >= 0:
659                line = line[1:b]
660            return line.strip()
661
662        #
663        # Need to add code to count matching '{' and '}' and if they
664        # do not match get the next line and add to the string until
665        # they match. This closes an opening '{' that is on another
666        # line.
667        #
668        for l in config:
669            self.lc += 1
670            l = _clean(l)
671            if len(l) == 0:
672                continue
673            log.trace('config: %s: %03d: %s %s' % \
674                          (self.init_name, self.lc, str(isvalid), l))
675            lo = l
676            if isvalid:
677                l = self._expand(l)
678            if len(l) == 0:
679                continue
680            if l[0] == '%':
681                ls = self.wss.split(l, 2)
682                los = self.wss.split(lo, 2)
683                if ls[0] == '%package':
684                    if isvalid:
685                        if ls[1] == '-n':
686                            name = ls[2]
687                        else:
688                            name = self.name + '-' + ls[1]
689                        return ('package', name)
690                elif ls[0] == '%disable':
691                    if isvalid:
692                        self._disable(config, ls)
693                elif ls[0] == '%select':
694                    if isvalid:
695                        self._select(config, ls)
696                elif ls[0] == '%error':
697                    if isvalid:
698                        return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))])
699                elif ls[0] == '%warning':
700                    if isvalid:
701                        return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))])
702                elif ls[0] == '%define' or ls[0] == '%global':
703                    if isvalid:
704                        self._define(config, ls)
705                elif ls[0] == '%undefine':
706                    if isvalid:
707                        self._undefine(config, ls)
708                elif ls[0] == '%if':
709                    d = self._if(config, ls, isvalid)
710                    if len(d):
711                        return ('data', d)
712                elif ls[0] == '%ifn':
713                    d = self._if(config, ls, isvalid, True)
714                    if len(d):
715                        return ('data', d)
716                elif ls[0] == '%ifos':
717                    d = self._ifos(config, ls, isvalid)
718                    if len(d):
719                        return ('data', d)
720                elif ls[0] == '%ifarch':
721                    d = self._ifarch(config, True, ls, isvalid)
722                    if len(d):
723                        return ('data', d)
724                elif ls[0] == '%ifnarch':
725                    d = self._ifarch(config, False, ls, isvalid)
726                    if len(d):
727                        return ('data', d)
728                elif ls[0] == '%endif':
729                    if roc:
730                        return ('control', '%endif', '%endif')
731                    log.warning("unexpected '" + ls[0] + "'")
732                elif ls[0] == '%else':
733                    if roc:
734                        return ('control', '%else', '%else')
735                    log.warning("unexpected '" + ls[0] + "'")
736                elif ls[0].startswith('%defattr'):
737                    return ('data', [l])
738                elif ls[0] == '%bcond_with':
739                    if isvalid:
740                        #
741                        # Check if already defined. Would be by the command line or
742                        # even a host specific default.
743                        #
744                        if self._label('with_' + ls[1]) not in self.macros:
745                            self._define(config, (ls[0], 'without_' + ls[1]))
746                elif ls[0] == '%bcond_without':
747                    if isvalid:
748                        if self._label('without_' + ls[1]) not in self.macros:
749                            self._define(config, (ls[0], 'with_' + ls[1]))
750                else:
751                    for r in self._ignore:
752                        if r.match(ls[0]) is not None:
753                            return ('data', [l])
754                    if isvalid:
755                        for d in self._directive:
756                            if ls[0].strip() == d:
757                                return ('directive', ls[0].strip(), ls[1:])
758                        log.warning("unknown directive: '" + ls[0] + "'")
759                        return ('data', [lo])
760            else:
761                return ('data', [lo])
762        return ('control', '%end', '%end')
763
764    def _set_package(self, _package):
765        if self.package == 'main' and \
766                self._packages[self.package].name() != None:
767            if self._packages[self.package].name() == _package:
768                return
769        if _package not in self._packages:
770            self._packages[_package] = package(_package,
771                                               self.define('%{_arch}'),
772                                               self)
773        self.package = _package
774
775    def _directive_extend(self, dir, data):
776        self._packages[self.package].directive_extend(dir, data)
777
778    def _info_append(self, info, data):
779        self._packages[self.package].info_append(info, data)
780
781    def load(self, name):
782
783        def common_end(left, right):
784            end = ''
785            while len(left) and len(right):
786                if left[-1] != right[-1]:
787                    return end
788                end = left[-1] + end
789                left = left[:-1]
790                right = right[:-1]
791            return end
792
793        if self.load_depth == 0:
794            self.in_error = False
795            self.lc = 0
796            self.name = name
797            self.conditionals = {}
798            self._packages = {}
799            self.package = 'main'
800            self._packages[self.package] = package(self.package,
801                                                   self.define('%{_arch}'),
802                                                   self)
803
804        self.load_depth += 1
805
806        save_name = self.name
807        save_lc = self.lc
808
809        self.name = name
810        self.lc = 0
811
812        #
813        # Locate the config file. Expand any macros then add the
814        # extension. Check if the file exists, therefore directly
815        # referenced. If not see if the file contains ':' or the path
816        # separator. If it does split the path else use the standard config dir
817        # path in the defaults.
818        #
819
820        exname = self.expand(name)
821
822        #
823        # Macro could add an extension.
824        #
825        if exname.endswith('.cfg'):
826            configname = exname
827        else:
828            configname = '%s.cfg' % (exname)
829            name = '%s.cfg' % (name)
830
831        if ':' in configname:
832            cfgname = path.basename(configname)
833        else:
834            cfgname = common_end(configname, name)
835
836        if not path.exists(configname):
837            if ':' in configname:
838                configdirs = path.dirname(configname).split(':')
839            else:
840                configdirs = self.define('_configdir').split(':')
841            for cp in configdirs:
842                configname = path.join(path.abspath(cp), cfgname)
843                if path.exists(configname):
844                    break
845                configname = None
846            if configname is None:
847                raise error.general('no config file found: %s' % (cfgname))
848
849        try:
850            log.trace('config: %s: _open: %s' % (self.init_name, path.host(configname)))
851            config = open(path.host(configname), 'r')
852        except IOError, err:
853            raise error.general('error opening config file: %s' % (path.host(configname)))
854        self.configpath += [configname]
855
856        self._includes += [configname]
857
858        try:
859            dir = None
860            info = None
861            data = []
862            while True:
863                r = self._parse(config)
864                if r[0] == 'package':
865                    self._set_package(r[1])
866                    dir = None
867                elif r[0] == 'control':
868                    if r[1] == '%end':
869                        break
870                    log.warning("unexpected '%s'" % (r[1]))
871                elif r[0] == 'directive':
872                    new_data = []
873                    if r[1] == '%description':
874                        new_data = [' '.join(r[2])]
875                    elif r[1] == '%include':
876                        self.load(r[2][0])
877                        continue
878                    else:
879                        if len(r[2]) == 0:
880                            _package = 'main'
881                        elif len(r[2]) == 1:
882                            _package = r[2][0]
883                        else:
884                            if r[2][0].strip() != '-n':
885                                log.warning("unknown directive option: '%s'" % (' '.join(r[2])))
886                            _package = r[2][1].strip()
887                        self._set_package(_package)
888                    if dir and dir != r[1]:
889                        self._directive_extend(dir, data)
890                    dir = r[1]
891                    data = new_data
892                elif r[0] == 'data':
893                    for l in r[1]:
894                        if l.startswith('%error'):
895                            l = self._expand(l)
896                            raise error.general('config error: %s' % (l[7:]))
897                        elif l.startswith('%warning'):
898                            l = self._expand(l)
899                            log.stderr('warning: %s' % (l[9:]))
900                            log.warning(l[9:])
901                        if not dir:
902                            l = self._expand(l)
903                            ls = self.tags.split(l, 1)
904                            log.trace('config: %s: _tag: %s %s' % (self.init_name, l, ls))
905                            if len(ls) > 1:
906                                info = ls[0].lower()
907                                if info[-1] == ':':
908                                    info = info[:-1]
909                                info_data = ls[1].strip()
910                            else:
911                                info_data = ls[0].strip()
912                            if info is not None:
913                                self._info_append(info, info_data)
914                            else:
915                                log.warning("invalid format: '%s'" % (info_data[:-1]))
916                        else:
917                            data.append(l)
918                else:
919                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
920            if dir is not None:
921                self._directive_extend(dir, data)
922        except:
923            config.close()
924            raise
925
926        config.close()
927
928        self.name = save_name
929        self.lc = save_lc
930
931        self.load_depth -= 1
932
933    def defined(self, name):
934        return self.macros.has_key(name)
935
936    def define(self, name):
937        if name in self.macros:
938            d = self.macros[name]
939        else:
940            n = self._label(name)
941            if n in self.macros:
942                d = self.macros[n]
943            else:
944                raise error.general('%d: macro "%s" not found' % (self.lc, name))
945        return self._expand(d)
946
947    def set_define(self, name, value):
948        self.macros[name] = value
949
950    def expand(self, line):
951        if type(line) == list:
952            el = []
953            for l in line:
954                el += [self._expand(l)]
955            return el
956        return self._expand(line)
957
958    def macro(self, name):
959        if name in self.macros:
960            return self.macros[name]
961        raise error.general('macro "%s" not found' % (name))
962
963    def directive(self, _package, name):
964        if _package not in self._packages:
965            raise error.general('package "' + _package + '" not found')
966        if name not in self._packages[_package].directives:
967            raise error.general('directive "' + name + \
968                                    '" not found in package "' + _package + '"')
969        return self._packages[_package].directives[name]
970
971    def abspath(self, rpath):
972        return path.abspath(self.define(rpath))
973
974    def packages(self):
975        return self._packages
976
977    def includes(self):
978        return self._includes
979
980def run():
981    import sys
982    try:
983        #
984        # Run where defaults.mc is located
985        #
986        opts = options.load(sys.argv, defaults = 'defaults.mc')
987        log.trace('config: count %d' % (len(opts.config_files())))
988        for config_file in opts.config_files():
989            s = file(config_file, opts)
990            print s
991            del s
992    except error.general, gerr:
993        print gerr
994        sys.exit(1)
995    except error.internal, ierr:
996        print ierr
997        sys.exit(1)
998    except KeyboardInterrupt:
999        log.notice('abort: user terminated')
1000        sys.exit(1)
1001    sys.exit(0)
1002
1003if __name__ == "__main__":
1004    run()
Note: See TracBrowser for help on using the repository browser.