source: rtems-source-builder/source-builder/sb/config.py @ 376aabf

4.104.114.95
Last change on this file since 376aabf was 376aabf, checked in by Chris Johns <chrisj@…>, on 04/13/13 at 00:39:02

Another defines bug fix.

  • 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.infos:
107            if expand:
108                return self.config.expand(self.infos[info])
109            else:
110                return self.infos[info]
111        return None
112
113    def extract_info(self, label, expand = True):
114        infos = {}
115        for i in self.infos:
116            il = i.lower()
117            if il.startswith(label):
118                if il == label:
119                    il = label + '0'
120                elif not il[len(label):].isdigit():
121                    continue
122                infos[il] = self.config.expand(self.infos[i])
123        return infos
124
125    def find_info(self, label, expand = True):
126        for i in self.infos:
127            if i.lower() == label:
128                if expand:
129                    return self.config.expand(self.infos[i])
130                else:
131                    return self.infos[i]
132        return None
133
134    def find_directive(self, label, expand = True):
135        for d in self.directives:
136            if d.lower() == label:
137                if expand:
138                    return self.config.expand(self.directives[d])
139                else:
140                    return self.directives[d]
141        return None
142
143    def name(self):
144        info = self.find_info('name')
145        if info:
146            n = info[0]
147        else:
148            n = self._name
149        return self._macro_override(n, 'name')
150
151    def summary(self):
152        info = self.find_info('summary')
153        if info:
154            return info[0]
155        return ''
156
157    def url(self):
158        info = self.find_info('url')
159        if info:
160            return info[0]
161        return ''
162
163    def version(self):
164        info = self.find_info('version')
165        if not info:
166            return None
167        return info[0]
168
169    def release(self):
170        info = self.find_info('release')
171        if not info:
172            return None
173        return info[0]
174
175    def buildarch(self):
176        info = self.find_info('buildarch')
177        if not info:
178            return self._arch
179        return info[0]
180
181    def sources(self):
182        return self.extract_info('source')
183
184    def patches(self):
185        return self.extract_info('patch')
186
187    def prep(self):
188        return self.find_directive('%prep')
189
190    def build(self):
191        return self.find_directive('%build')
192
193    def install(self):
194        return self.find_directive('%install')
195
196    def clean(self):
197        return self.find_directive('%clean')
198
199    def include(self):
200        return self.find_directive('%include')
201
202    def long_name(self):
203        return self.name()
204
205class file:
206    """Parse a config file."""
207
208    _directive = [ '%description',
209                   '%prep',
210                   '%build',
211                   '%clean',
212                   '%install',
213                   '%include',
214                   '%install' ]
215
216    _ignore = [ re.compile('%setup'),
217                re.compile('%configure'),
218                re.compile('%source[0-9]*'),
219                re.compile('%patch[0-9]*'),
220                re.compile('%select') ]
221
222    def __init__(self, name, opts, macros = None):
223        self.opts = opts
224        if macros is None:
225            self.macros = opts.defaults
226        else:
227            self.macros = macros
228        if self.opts.trace():
229            print 'config: %s' % (name)
230        self.disable_macro_reassign = False
231        self.configpath = []
232        self.wss = re.compile(r'\s+')
233        self.tags = re.compile(r':+')
234        self.sf = re.compile(r'%\([^\)]+\)')
235        for arg in self.opts.args:
236            if arg.startswith('--with-') or arg.startswith('--without-'):
237                label = arg[2:].lower().replace('-', '_')
238                self.macros.define(label)
239        self._includes = []
240        self.load_depth = 0
241        self.load(name)
242
243    def __str__(self):
244
245        def _dict(dd):
246            s = ''
247            ddl = dd.keys()
248            ddl.sort()
249            for d in ddl:
250                s += '  ' + d + ': ' + dd[d] + '\n'
251            return s
252
253        s = 'config: %s' % ('.'.join(self.configpath)) + \
254            '\n' + str(self.opts) + \
255            '\nlines parsed: %d' % (self.lc) + \
256            '\nname: ' + self.name + \
257            '\nmacros:\n' + str(self.macros)
258        for _package in self._packages:
259            s += str(self._packages[_package])
260        return s
261
262    def _name_line_msg(self,  msg):
263        return '%s:%d: %s' % (path.basename(self.name), self.lc,  msg)
264
265    def _output(self, text):
266        if not self.opts.quiet():
267            log.output(text)
268
269    def _warning(self, msg):
270        self._output('warning: %s' % (self._name_line_msg(msg)))
271
272    def _error(self, msg):
273        err = 'error: %s' % (self._name_line_msg(msg))
274        print >> sys.stderr, err
275        self._output(err)
276        self.in_error = True
277        if not self.opts.dry_run():
278            print >> sys.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                        self._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                        self._warning("malformed conditional macro '%s'" % (m))
410                        mn = None
411                    else:
412                        e = self._expand(m[6:-1].strip())
413                        self._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                            self._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 _select(self, config, ls):
474        if len(ls) != 2:
475            self._warning('invalid select statement')
476        else:
477            r = self.macros.set_read_map(ls[1])
478            if self.opts.trace():
479                print '_select: ', r, ls[1], self.macros.maps()
480
481    def _define(self, config, ls):
482        if len(ls) <= 1:
483            self._warning('invalid macro definition')
484        else:
485            d = self._label(ls[1])
486            if self.disable_macro_reassign:
487                if (d not in self.macros) or \
488                        (d in self.macros and len(self.macros[d]) == 0):
489                    if len(ls) == 2:
490                        self.macros[d] = '1'
491                    else:
492                        self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
493                else:
494                    self._warning("macro '%s' already defined" % (d))
495            else:
496                if len(ls) == 2:
497                    self.macros[d] = '1'
498                else:
499                    self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
500
501    def _undefine(self, config, ls):
502        if len(ls) <= 1:
503            self._warning('invalid macro definition')
504        else:
505            mn = self._label(ls[1])
506            if mn in self.macros:
507                del self.macros[mn]
508            else:
509                self._warning("macro '%s' not defined" % (mn))
510
511    def _ifs(self, config, ls, label, iftrue, isvalid):
512        text = []
513        in_iftrue = True
514        while True:
515            if isvalid and \
516                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
517                this_isvalid = True
518            else:
519                this_isvalid = False
520            r = self._parse(config, roc = True, isvalid = this_isvalid)
521            if r[0] == 'control':
522                if r[1] == '%end':
523                    self._error(label + ' without %endif')
524                    raise error.general('terminating build')
525                if r[1] == '%endif':
526                    return text
527                if r[1] == '%else':
528                    in_iftrue = False
529            elif r[0] == 'data':
530                if this_isvalid:
531                    text.extend(r[1])
532
533    def _if(self, config, ls, isvalid, invert = False):
534
535        def add(x, y):
536            return x + ' ' + str(y)
537
538        istrue = False
539        if isvalid:
540            if len(ls) == 2:
541                s = ls[1]
542            else:
543                s = (ls[1] + ' ' + ls[2])
544            ifls = s.split()
545            if len(ifls) == 1:
546                #
547                # Check if '%if %{x} == %{nil}' has both parts as nothing
548                # which means '%if ==' is always True and '%if !=' is always false.
549                #
550                if ifls[0] == '==':
551                    istrue = True
552                elif ifls[0] == '!=':
553                    istrue = False
554                else:
555                    istrue = _check_bool(ifls[0])
556                    if istrue == None:
557                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
558                        istrue = False
559            elif len(ifls) == 2:
560                if ifls[0] == '!':
561                    istrue = _check_bool(ifls[1])
562                    if istrue == None:
563                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
564                        istrue = False
565                    else:
566                        istrue = not istrue
567                else:
568                    #
569                    # Check is something is being checked against empty,
570                    #   ie '%if %{x} == %{nil}'
571                    # The logic is 'something == nothing' is False and
572                    # 'something != nothing' is True.
573                    #
574                    if ifls[1] == '==':
575                        istrue = False
576                    elif  ifls[1] == '!=':
577                        istrue = True
578                    else:
579                        self._error('invalid if bool operator: ' + reduce(add, ls, ''))
580            elif len(ifls) == 3:
581                if ifls[1] == '==':
582                    if ifls[0] == ifls[2]:
583                        istrue = True
584                    else:
585                        istrue = False
586                elif ifls[1] == '!=' or ifls[1] == '=!':
587                    if ifls[0] != ifls[2]:
588                        istrue = True
589                    else:
590                        istrue = False
591                elif 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] == '<=' or ifls[1] == '=<':
602                    if ifls[0] <= ifls[2]:
603                        istrue = True
604                    else:
605                        istrue = False
606                elif ifls[1] == '<':
607                    if ifls[0] < ifls[2]:
608                        istrue = True
609                    else:
610                        istrue = False
611                else:
612                    self._error('invalid %if operator: ' + reduce(add, ls, ''))
613            else:
614                self._error('malformed if: ' + reduce(add, ls, ''))
615            if invert:
616                istrue = not istrue
617            if self.opts.trace():
618                print '_if:  ', ifls, istrue
619        return self._ifs(config, ls, '%if', istrue, isvalid)
620
621    def _ifos(self, config, ls, isvalid):
622        isos = False
623        if isvalid:
624            os = self.define('_os')
625            for l in ls:
626                if l in os:
627                    isos = True
628                    break
629        return self._ifs(config, ls, '%ifos', isos, isvalid)
630
631    def _ifarch(self, config, positive, ls, isvalid):
632        isarch = False
633        if isvalid:
634            arch = self.define('_arch')
635            for l in ls:
636                if l in arch:
637                    isarch = True
638                    break
639        if not positive:
640            isarch = not isarch
641        return self._ifs(config, ls, '%ifarch', isarch, isvalid)
642
643    def _parse(self, config, roc = False, isvalid = True):
644        # roc = return on control
645
646        def _clean(line):
647            line = line[0:-1]
648            b = line.find('#')
649            if b >= 0:
650                line = line[1:b]
651            return line.strip()
652
653        #
654        # Need to add code to count matching '{' and '}' and if they
655        # do not match get the next line and add to the string until
656        # they match. This closes an opening '{' that is on another
657        # line.
658        #
659        for l in config:
660            self.lc += 1
661            l = _clean(l)
662            if len(l) == 0:
663                continue
664            if self.opts.trace():
665                print '%03d: %d %s' % (self.lc, isvalid, l)
666            lo = l
667            if isvalid:
668                l = self._expand(l)
669            if len(l) == 0:
670                continue
671            if l[0] == '%':
672                ls = self.wss.split(l, 2)
673                los = self.wss.split(lo, 2)
674                if ls[0] == '%package':
675                    if isvalid:
676                        if ls[1] == '-n':
677                            name = ls[2]
678                        else:
679                            name = self.name + '-' + ls[1]
680                        return ('package', name)
681                elif ls[0] == '%select':
682                    if isvalid:
683                        self._select(config, ls)
684                elif ls[0] == '%error':
685                    if isvalid:
686                        return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))])
687                elif ls[0] == '%warning':
688                    if isvalid:
689                        return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))])
690                elif ls[0] == '%define' or ls[0] == '%global':
691                    if isvalid:
692                        self._define(config, ls)
693                elif ls[0] == '%undefine':
694                    if isvalid:
695                        self._undefine(config, ls)
696                elif ls[0] == '%if':
697                    d = self._if(config, ls, isvalid)
698                    if len(d):
699                        return ('data', d)
700                elif ls[0] == '%ifn':
701                    d = self._if(config, ls, isvalid, True)
702                    if len(d):
703                        return ('data', d)
704                elif ls[0] == '%ifos':
705                    d = self._ifos(config, ls, isvalid)
706                    if len(d):
707                        return ('data', d)
708                elif ls[0] == '%ifarch':
709                    d = self._ifarch(config, True, ls, isvalid)
710                    if len(d):
711                        return ('data', d)
712                elif ls[0] == '%ifnarch':
713                    d = self._ifarch(config, False, ls, isvalid)
714                    if len(d):
715                        return ('data', d)
716                elif ls[0] == '%endif':
717                    if roc:
718                        return ('control', '%endif', '%endif')
719                    self._warning("unexpected '" + ls[0] + "'")
720                elif ls[0] == '%else':
721                    if roc:
722                        return ('control', '%else', '%else')
723                    self._warning("unexpected '" + ls[0] + "'")
724                elif ls[0].startswith('%defattr'):
725                    return ('data', [l])
726                elif ls[0] == '%bcond_with':
727                    if isvalid:
728                        #
729                        # Check if already defined. Would be by the command line or
730                        # even a host specific default.
731                        #
732                        if self._label('with_' + ls[1]) not in self.macros:
733                            self._define(config, (ls[0], 'without_' + ls[1]))
734                elif ls[0] == '%bcond_without':
735                    if isvalid:
736                        if self._label('without_' + ls[1]) not in self.macros:
737                            self._define(config, (ls[0], 'with_' + ls[1]))
738                else:
739                    for r in self._ignore:
740                        if r.match(ls[0]) is not None:
741                            return ('data', [l])
742                    if isvalid:
743                        for d in self._directive:
744                            if ls[0].strip() == d:
745                                return ('directive', ls[0].strip(), ls[1:])
746                        self._warning("unknown directive: '" + ls[0] + "'")
747                        return ('data', [lo])
748            else:
749                return ('data', [lo])
750        return ('control', '%end', '%end')
751
752    def _set_package(self, _package):
753        if self.package == 'main' and \
754                self._packages[self.package].name() != None:
755            if self._packages[self.package].name() == _package:
756                return
757        if _package not in self._packages:
758            self._packages[_package] = package(_package,
759                                               self.define('%{_arch}'),
760                                               self)
761        self.package = _package
762
763    def _directive_extend(self, dir, data):
764        self._packages[self.package].directive_extend(dir, data)
765
766    def _info_append(self, info, data):
767        self._packages[self.package].info_append(info, data)
768
769    def load(self, name):
770
771        def common_end(left, right):
772            end = ''
773            while len(left) and len(right):
774                if left[-1] != right[-1]:
775                    return end
776                end = left[-1] + end
777                left = left[:-1]
778                right = right[:-1]
779            return end
780
781        if self.load_depth == 0:
782            self.in_error = False
783            self.lc = 0
784            self.name = name
785            self.conditionals = {}
786            self._packages = {}
787            self.package = 'main'
788            self._packages[self.package] = package(self.package,
789                                                   self.define('%{_arch}'),
790                                                   self)
791
792        self.load_depth += 1
793
794        save_name = self.name
795        save_lc = self.lc
796
797        self.name = name
798        self.lc = 0
799
800        #
801        # Locate the config file. Expand any macros then add the
802        # extension. Check if the file exists, therefore directly
803        # referenced. If not see if the file contains ':' or the path
804        # separator. If it does split the path else use the standard config dir
805        # path in the defaults.
806        #
807
808        exname = self.expand(name)
809
810        #
811        # Macro could add an extension.
812        #
813        if exname.endswith('.cfg'):
814            configname = exname
815        else:
816            configname = '%s.cfg' % (exname)
817            name = '%s.cfg' % (name)
818
819        if ':' in configname:
820            cfgname = path.basename(configname)
821        else:
822            cfgname = common_end(configname, name)
823
824        if not path.exists(configname):
825            if ':' in configname:
826                configdirs = path.dirname(configname).split(':')
827            else:
828                configdirs = self.define('_configdir').split(':')
829            for cp in configdirs:
830                configname = path.join(path.abspath(cp), cfgname)
831                if path.exists(configname):
832                    break
833                configname = None
834            if configname is None:
835                raise error.general('no config file found: %s' % (cfgname))
836
837        try:
838            if self.opts.trace():
839                print '_open: %s' % (path.host(configname))
840            config = open(path.host(configname), 'r')
841        except IOError, err:
842            raise error.general('error opening config file: %s' % (path.host(configname)))
843        self.configpath += [configname]
844
845        self._includes += [configname]
846
847        try:
848            dir = None
849            info = None
850            data = []
851            while True:
852                r = self._parse(config)
853                if r[0] == 'package':
854                    self._set_package(r[1])
855                    dir = None
856                elif r[0] == 'control':
857                    if r[1] == '%end':
858                        break
859                    self._warning("unexpected '%s'" % (r[1]))
860                elif r[0] == 'directive':
861                    new_data = []
862                    if r[1] == '%description':
863                        new_data = [' '.join(r[2])]
864                    elif r[1] == '%include':
865                        self.load(r[2][0])
866                        continue
867                    else:
868                        if len(r[2]) == 0:
869                            _package = 'main'
870                        elif len(r[2]) == 1:
871                            _package = r[2][0]
872                        else:
873                            if r[2][0].strip() != '-n':
874                                self._warning("unknown directive option: '%s'" % (' '.join(r[2])))
875                            _package = r[2][1].strip()
876                        self._set_package(_package)
877                    if dir and dir != r[1]:
878                        self._directive_extend(dir, data)
879                    dir = r[1]
880                    data = new_data
881                elif r[0] == 'data':
882                    for l in r[1]:
883                        if l.startswith('%error'):
884                            l = self._expand(l)
885                            raise error.general('config error: %s' % (l[7:]))
886                        elif l.startswith('%warning'):
887                            l = self._expand(l)
888                            print >> sys.stderr, 'warning: %s' % (l[9:])
889                            self._warning(l[9:])
890                        if not dir:
891                            l = self._expand(l)
892                            ls = self.tags.split(l, 1)
893                            if self.opts.trace():
894                                print '_tag: ', l, ls
895                            if len(ls) > 1:
896                                info = ls[0].lower()
897                                if info[-1] == ':':
898                                    info = info[:-1]
899                                info_data = ls[1].strip()
900                            else:
901                                info_data = ls[0].strip()
902                            if info is not None:
903                                self._info_append(info, info_data)
904                            else:
905                                self._warning("invalid format: '%s'" % (info_data[:-1]))
906                        else:
907                            data.append(l)
908                else:
909                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
910            if dir is not None:
911                self._directive_extend(dir, data)
912        except:
913            config.close()
914            raise
915
916        config.close()
917
918        self.name = save_name
919        self.lc = save_lc
920
921        self.load_depth -= 1
922
923    def defined(self, name):
924        return self.macros.has_key(name)
925
926    def define(self, name):
927        if name in self.macros:
928            d = self.macros[name]
929        else:
930            n = self._label(name)
931            if n in self.macros:
932                d = self.macros[n]
933            else:
934                raise error.general('%d: macro "%s" not found' % (self.lc, name))
935        return self._expand(d)
936
937    def set_define(self, name, value):
938        self.macros[name] = value
939
940    def expand(self, line):
941        if type(line) == list:
942            el = []
943            for l in line:
944                el += [self._expand(l)]
945            return el
946        return self._expand(line)
947
948    def macro(self, name):
949        if name in self.macros:
950            return self.macros[name]
951        raise error.general('macro "%s" not found' % (name))
952
953    def directive(self, _package, name):
954        if _package not in self._packages:
955            raise error.general('package "' + _package + '" not found')
956        if name not in self._packages[_package].directives:
957            raise error.general('directive "' + name + \
958                                    '" not found in package "' + _package + '"')
959        return self._packages[_package].directives[name]
960
961    def abspath(self, rpath):
962        return path.abspath(self.define(rpath))
963
964    def packages(self):
965        return self._packages
966
967    def includes(self):
968        return self._includes
969
970def run():
971    import sys
972    try:
973        #
974        # Run where defaults.mc is located
975        #
976        opts = options.load(sys.argv, defaults = 'defaults.mc')
977        if opts.trace():
978            print 'config: count %d' % (len(opts.config_files()))
979        for config_file in opts.config_files():
980            s = file(config_file, opts)
981            print s
982            del s
983    except error.general, gerr:
984        print gerr
985        sys.exit(1)
986    except error.internal, ierr:
987        print ierr
988        sys.exit(1)
989    except KeyboardInterrupt:
990        print 'user terminated'
991        sys.exit(1)
992    sys.exit(0)
993
994if __name__ == "__main__":
995    run()
Note: See TracBrowser for help on using the repository browser.