source: rtems-source-builder/source-builder/sb/config.py @ ec56744

4.104.114.95
Last change on this file since ec56744 was b8ee74d, checked in by Chris Johns <chrisj@…>, on 04/15/13 at 03:26:36

Add a %testing section to the configuration files.

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