source: rtems-source-builder/source-builder/sb/config.py @ 5142bec

4.104.114.95
Last change on this file since 5142bec was 5142bec, checked in by Chris Johns <chrisj@…>, on 04/21/13 at 08:37:02

Refactor the logging support.

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