source: rtems-source-builder/source-builder/sb/config.py @ 0565e1f

4.104.114.95
Last change on this file since 0565e1f was 0565e1f, checked in by Chris Johns <chrisj@…>, on 04/13/13 at 08:29:30

Add support for snapshot testing.

User macro files passed on the command line allow a user to
override the defaults in configuration files to test new changes
in pending releases.

Fix macros issues with keys with more than one map.

  • Property mode set to 100644
File size: 34.7 KB
RevLine 
[bf13d27]1#
2# RTEMS Tools Project (http://www.rtems.org/)
[cb12e48]3# Copyright 2010-2013 Chris Johns (chrisj@rtems.org)
[bf13d27]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
[4f26bdb]28import copy
[bf13d27]29import os
30import re
31import sys
32
[c18c4b6]33try:
34    import error
35    import execute
36    import log
[cb12e48]37    import options
[c18c4b6]38    import path
39except KeyboardInterrupt:
40    print 'user terminated'
41    sys.exit(1)
42except:
[aabd20d]43    print 'error: unknown application load error'
[c18c4b6]44    sys.exit(1)
[bf13d27]45
[c096c20]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
[bf13d27]56class package:
57
[4f26bdb]58    def __init__(self, name, arch, config):
[bf13d27]59        self._name = name
60        self._arch = arch
[4f26bdb]61        self.config = config
[bf13d27]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
[5c7b1ef]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
[bf13d27]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)
[5c7b1ef]97        self.config.macros[dir] = '\n'.join(self.directives[dir])
[bf13d27]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)
[5c7b1ef]103        self.config.macros[info] = '\n'.join(self.infos[info])
[bf13d27]104
[4f26bdb]105    def get_info(self, info, expand = True):
[0565e1f]106        if info in self.config.macros:
107            _info = self.config.macros[info].split('\n')
[4f26bdb]108            if expand:
[0565e1f]109                return self.config.expand(_info)
[4f26bdb]110            else:
[0565e1f]111                return _info
[3b0ce4e]112        return None
[bf13d27]113
[4f26bdb]114    def extract_info(self, label, expand = True):
[0565e1f]115        ll = label.lower()
[bf13d27]116        infos = {}
[0565e1f]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])]
[bf13d27]124        return infos
125
[0565e1f]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
[bf13d27]133        return None
134
[0565e1f]135    def find_info(self, label, expand = True):
136        return self._find_macro(label, expand)
137
[4f26bdb]138    def find_directive(self, label, expand = True):
[0565e1f]139        return self._find_macro(label, expand)
[bf13d27]140
141    def name(self):
142        info = self.find_info('name')
143        if info:
[5c7b1ef]144            n = info[0]
145        else:
146            n = self._name
147        return self._macro_override(n, 'name')
[bf13d27]148
[3b0ce4e]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
[bf13d27]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):
[5c7b1ef]180        return self.extract_info('source')
[bf13d27]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 long_name(self):
201        return self.name()
202
203class file:
204    """Parse a config file."""
205
206    _directive = [ '%description',
207                   '%prep',
208                   '%build',
209                   '%clean',
[9c2fe14]210                   '%install',
211                   '%include',
212                   '%install' ]
[bf13d27]213
214    _ignore = [ re.compile('%setup'),
215                re.compile('%configure'),
216                re.compile('%source[0-9]*'),
[5c7b1ef]217                re.compile('%patch[0-9]*'),
218                re.compile('%select') ]
[bf13d27]219
[cb12e48]220    def __init__(self, name, opts, macros = None):
[bf13d27]221        self.opts = opts
[cb12e48]222        if macros is None:
223            self.macros = opts.defaults
224        else:
225            self.macros = macros
[bf13d27]226        if self.opts.trace():
227            print 'config: %s' % (name)
[50da39a]228        self.disable_macro_reassign = False
[bf13d27]229        self.configpath = []
230        self.wss = re.compile(r'\s+')
231        self.tags = re.compile(r':+')
232        self.sf = re.compile(r'%\([^\)]+\)')
233        for arg in self.opts.args:
234            if arg.startswith('--with-') or arg.startswith('--without-'):
235                label = arg[2:].lower().replace('-', '_')
[cb12e48]236                self.macros.define(label)
[79f80fd]237        self._includes = []
[bf13d27]238        self.load_depth = 0
239        self.load(name)
240
241    def __str__(self):
242
243        def _dict(dd):
244            s = ''
245            ddl = dd.keys()
246            ddl.sort()
247            for d in ddl:
248                s += '  ' + d + ': ' + dd[d] + '\n'
249            return s
250
251        s = 'config: %s' % ('.'.join(self.configpath)) + \
252            '\n' + str(self.opts) + \
253            '\nlines parsed: %d' % (self.lc) + \
254            '\nname: ' + self.name + \
[cb12e48]255            '\nmacros:\n' + str(self.macros)
[bf13d27]256        for _package in self._packages:
257            s += str(self._packages[_package])
258        return s
259
[5260449]260    def _name_line_msg(self,  msg):
[ab8319a]261        return '%s:%d: %s' % (path.basename(self.name), self.lc,  msg)
[5260449]262
[bf13d27]263    def _output(self, text):
264        if not self.opts.quiet():
265            log.output(text)
266
267    def _warning(self, msg):
[5260449]268        self._output('warning: %s' % (self._name_line_msg(msg)))
[bf13d27]269
270    def _error(self, msg):
[5260449]271        err = 'error: %s' % (self._name_line_msg(msg))
272        print >> sys.stderr, err
273        self._output(err)
[bf13d27]274        self.in_error = True
275        if not self.opts.dry_run():
276            print >> sys.stderr, 'warning: switched to dry run due to errors'
277            self.opts.set_dry_run()
278
279    def _label(self, name):
[cb12e48]280        if name.startswith('%{') and name[-1] is '}':
281            return name
[bf13d27]282        return '%{' + name.lower() + '}'
283
284    def _macro_split(self, s):
285        '''Split the string (s) up by macros. Only split on the
286           outter level. Nested levels will need to split with futher calls.'''
287        trace_me = False
[4f26bdb]288        if trace_me:
289            print '------------------------------------------------------'
[bf13d27]290        macros = []
291        nesting = []
292        has_braces = False
293        c = 0
294        while c < len(s):
295            if trace_me:
296                print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting
297            #
298            # We need to watch for shell type variables or the form '${var}' because
299            # they can upset the brace matching.
300            #
301            if s[c] == '%' or s[c] == '$':
302                start = s[c]
303                c += 1
304                if c == len(s):
305                    continue
306                #
307                # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ?
308                #
309                if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'):
310                    continue
311                elif not s[c].isspace():
312                    #
313                    # If this is a shell macro and we are at the outter
314                    # level or is '$var' forget it and move on.
315                    #
316                    if start == '$' and (s[c] != '{' or len(nesting) == 0):
317                        continue
318                    if s[c] == '{':
319                        this_has_braces = True
320                    else:
321                        this_has_braces = False
322                    nesting.append((c - 1, has_braces))
323                    has_braces = this_has_braces
324            elif len(nesting) > 0:
325                if s[c] == '}' or (s[c].isspace() and not has_braces):
326                    #
327                    # Can have '%{?test: something %more}' where the
328                    # nested %more ends with the '}' which also ends
329                    # the outter macro.
330                    #
331                    if not has_braces:
332                        if s[c] == '}':
333                            macro_start, has_braces = nesting[len(nesting) - 1]
334                            nesting = nesting[:-1]
335                            if len(nesting) == 0:
336                                macros.append(s[macro_start:c].strip())
337                    if len(nesting) > 0:
338                        macro_start, has_braces = nesting[len(nesting) - 1]
339                        nesting = nesting[:-1]
340                        if len(nesting) == 0:
341                            macros.append(s[macro_start:c + 1].strip())
342            c += 1
343        if trace_me:
344            print 'ms:', macros
[4f26bdb]345        if trace_me:
346            print '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='
[bf13d27]347        return macros
348
349    def _shell(self, line):
350        sl = self.sf.findall(line)
351        if len(sl):
352            e = execute.capture_execution()
353            for s in sl:
354                exit_code, proc, output = e.shell(s[2:-1])
355                if exit_code == 0:
356                    line = line.replace(s, output)
357                else:
[e4cb1d0]358                    raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output))
[bf13d27]359        return line
360
361    def _expand(self, s):
[4f26bdb]362        expand_count = 0
[bf13d27]363        expanded = True
364        while expanded:
[4f26bdb]365            expand_count += 1
366            if expand_count > 500:
367                raise error.general('macro expand looping: %s' % (s))
[bf13d27]368            expanded = False
369            ms = self._macro_split(s)
370            for m in ms:
371                mn = m
372                #
373                # A macro can be '%{macro}' or '%macro'. Turn the later into
374                # the former.
375                #
376                show_warning = True
377                if mn[1] != '{':
378                    for r in self._ignore:
379                        if r.match(mn) is not None:
380                            mn = None
381                            break
382                    else:
383                        mn = self._label(mn[1:])
384                        show_warning = False
385                elif m.startswith('%{expand'):
386                    colon = m.find(':')
387                    if colon < 8:
388                        self._warning('malformed expand macro, no colon found')
389                    else:
390                        e = self._expand(m[colon + 1:-1].strip())
391                        s = s.replace(m, e)
392                        expanded = True
393                        mn = None
394                elif m.startswith('%{with '):
395                    #
396                    # Change the ' ' to '_' because the macros have no spaces.
397                    #
398                    n = self._label('with_' + m[7:-1].strip())
[cb12e48]399                    if n in self.macros:
[bf13d27]400                        s = s.replace(m, '1')
401                    else:
402                        s = s.replace(m, '0')
403                    expanded = True
404                    mn = None
405                elif m.startswith('%{echo'):
[50da39a]406                    if not m.endswith('}'):
407                        self._warning("malformed conditional macro '%s'" % (m))
408                        mn = None
409                    else:
410                        e = self._expand(m[6:-1].strip())
411                        self._output('%s' % (self._name_line_msg(e)))
412                        s = ''
413                        expanded = True
414                        mn = None
[bf13d27]415                elif m.startswith('%{defined'):
416                    n = self._label(m[9:-1].strip())
[cb12e48]417                    if n in self.macros:
[bf13d27]418                        s = s.replace(m, '1')
419                    else:
420                        s = s.replace(m, '0')
421                    expanded = True
422                    mn = None
423                elif m.startswith('%{?') or m.startswith('%{!?'):
424                    if m[2] == '!':
425                        start = 4
426                    else:
427                        start = 3
428                    colon = m[start:].find(':')
429                    if colon < 0:
430                        if not m.endswith('}'):
[87118b9]431                            self._warning("malformed conditional macro '%s'" % (m))
[bf13d27]432                            mn = None
433                        else:
434                            mn = self._label(m[start:-1])
435                    else:
436                        mn = self._label(m[start:start + colon])
437                    if mn:
438                        if m.startswith('%{?'):
[c096c20]439                            istrue = False
[cb12e48]440                            if mn in self.macros:
[45ca8cf]441                                # If defined and 0 then it is false.
[cb12e48]442                                istrue = _check_bool(self.macros[mn])
[c096c20]443                                if istrue is None:
[45ca8cf]444                                    istrue = True
[c096c20]445                            if colon >= 0 and istrue:
446                                s = s.replace(m, m[start + colon + 1:-1])
447                                expanded = True
448                                mn = None
[3b0ce4e]449                            elif not istrue:
[bf13d27]450                                mn = '%{nil}'
451                        else:
[c096c20]452                            isfalse = True
[cb12e48]453                            if mn in self.macros:
454                                istrue = _check_bool(self.macros[mn])
[45ca8cf]455                                if istrue is None or istrue == True:
[c096c20]456                                    isfalse = False
457                            if colon >= 0 and isfalse:
458                                s = s.replace(m, m[start + colon + 1:-1])
459                                expanded = True
460                                mn = None
[bf13d27]461                            else:
462                                mn = '%{nil}'
463                if mn:
[cb12e48]464                    if mn.lower() in self.macros:
465                        s = s.replace(m, self.macros[mn.lower()])
[bf13d27]466                        expanded = True
467                    elif show_warning:
[ab8319a]468                        self._error("macro '%s' not found" % (mn))
[bf13d27]469        return self._shell(s)
470
[5c7b1ef]471    def _select(self, config, ls):
472        if len(ls) != 2:
473            self._warning('invalid select statement')
474        else:
475            r = self.macros.set_read_map(ls[1])
476            if self.opts.trace():
477                print '_select: ', r, ls[1], self.macros.maps()
478
[bf13d27]479    def _define(self, config, ls):
480        if len(ls) <= 1:
481            self._warning('invalid macro definition')
482        else:
483            d = self._label(ls[1])
[50da39a]484            if self.disable_macro_reassign:
[cb12e48]485                if (d not in self.macros) or \
486                        (d in self.macros and len(self.macros[d]) == 0):
[50da39a]487                    if len(ls) == 2:
[cb12e48]488                        self.macros[d] = '1'
[50da39a]489                    else:
[cb12e48]490                        self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
[50da39a]491                else:
492                    self._warning("macro '%s' already defined" % (d))
493            else:
[bf13d27]494                if len(ls) == 2:
[cb12e48]495                    self.macros[d] = '1'
[bf13d27]496                else:
[cb12e48]497                    self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
[bf13d27]498
499    def _undefine(self, config, ls):
500        if len(ls) <= 1:
501            self._warning('invalid macro definition')
502        else:
503            mn = self._label(ls[1])
[cb12e48]504            if mn in self.macros:
505                del self.macros[mn]
[50da39a]506            else:
507                self._warning("macro '%s' not defined" % (mn))
[bf13d27]508
509    def _ifs(self, config, ls, label, iftrue, isvalid):
510        text = []
511        in_iftrue = True
512        while True:
513            if isvalid and \
514                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
515                this_isvalid = True
516            else:
517                this_isvalid = False
518            r = self._parse(config, roc = True, isvalid = this_isvalid)
519            if r[0] == 'control':
520                if r[1] == '%end':
521                    self._error(label + ' without %endif')
522                    raise error.general('terminating build')
523                if r[1] == '%endif':
524                    return text
525                if r[1] == '%else':
526                    in_iftrue = False
527            elif r[0] == 'data':
528                if this_isvalid:
529                    text.extend(r[1])
530
531    def _if(self, config, ls, isvalid, invert = False):
532
533        def add(x, y):
534            return x + ' ' + str(y)
535
536        istrue = False
537        if isvalid:
538            if len(ls) == 2:
539                s = ls[1]
540            else:
541                s = (ls[1] + ' ' + ls[2])
542            ifls = s.split()
543            if len(ifls) == 1:
544                #
545                # Check if '%if %{x} == %{nil}' has both parts as nothing
546                # which means '%if ==' is always True and '%if !=' is always false.
547                #
548                if ifls[0] == '==':
549                    istrue = True
[99eee0e]550                elif ifls[0] == '!=':
[bf13d27]551                    istrue = False
552                else:
[c096c20]553                    istrue = _check_bool(ifls[0])
[bf13d27]554                    if istrue == None:
555                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
556                        istrue = False
557            elif len(ifls) == 2:
558                if ifls[0] == '!':
[c096c20]559                    istrue = _check_bool(ifls[1])
[bf13d27]560                    if istrue == None:
561                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
562                        istrue = False
563                    else:
564                        istrue = not istrue
565                else:
566                    #
567                    # Check is something is being checked against empty,
568                    #   ie '%if %{x} == %{nil}'
569                    # The logic is 'something == nothing' is False and
570                    # 'something != nothing' is True.
571                    #
572                    if ifls[1] == '==':
573                        istrue = False
574                    elif  ifls[1] == '!=':
575                        istrue = True
576                    else:
577                        self._error('invalid if bool operator: ' + reduce(add, ls, ''))
578            elif len(ifls) == 3:
579                if ifls[1] == '==':
580                    if ifls[0] == ifls[2]:
581                        istrue = True
582                    else:
583                        istrue = False
584                elif ifls[1] == '!=' or ifls[1] == '=!':
585                    if ifls[0] != ifls[2]:
586                        istrue = True
587                    else:
588                        istrue = False
589                elif ifls[1] == '>':
590                    if ifls[0] > ifls[2]:
591                        istrue = True
592                    else:
593                        istrue = False
594                elif ifls[1] == '>=' or ifls[1] == '=>':
595                    if ifls[0] >= ifls[2]:
596                        istrue = True
597                    else:
598                        istrue = False
599                elif ifls[1] == '<=' or ifls[1] == '=<':
600                    if ifls[0] <= ifls[2]:
601                        istrue = True
602                    else:
603                        istrue = False
604                elif ifls[1] == '<':
605                    if ifls[0] < ifls[2]:
606                        istrue = True
607                    else:
608                        istrue = False
609                else:
610                    self._error('invalid %if operator: ' + reduce(add, ls, ''))
611            else:
612                self._error('malformed if: ' + reduce(add, ls, ''))
613            if invert:
614                istrue = not istrue
615            if self.opts.trace():
616                print '_if:  ', ifls, 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')
[4f26bdb]623            for l in ls:
624                if l in os:
625                    isos = True
626                    break
[bf13d27]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')
[4f26bdb]633            for l in ls:
634                if l in arch:
635                    isarch = True
636                    break
[bf13d27]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            if self.opts.trace():
663                print '%03d: %d %s' % (self.lc, isvalid, l)
[4f26bdb]664            lo = l
[bf13d27]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)
[4f26bdb]671                los = self.wss.split(lo, 2)
[bf13d27]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)
[5c7b1ef]679                elif ls[0] == '%select':
680                    if isvalid:
681                        self._select(config, ls)
[bf13d27]682                elif ls[0] == '%error':
683                    if isvalid:
[5260449]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:]))])
[bf13d27]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:
[4f26bdb]716                        return ('control', '%endif', '%endif')
[bf13d27]717                    self._warning("unexpected '" + ls[0] + "'")
718                elif ls[0] == '%else':
719                    if roc:
[4f26bdb]720                        return ('control', '%else', '%else')
[bf13d27]721                    self._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                        #
[cb12e48]730                        if self._label('with_' + ls[1]) not in self.macros:
[bf13d27]731                            self._define(config, (ls[0], 'without_' + ls[1]))
732                elif ls[0] == '%bcond_without':
733                    if isvalid:
[cb12e48]734                        if self._label('without_' + ls[1]) not in self.macros:
[bf13d27]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                        self._warning("unknown directive: '" + ls[0] + "'")
[4f26bdb]745                        return ('data', [lo])
[bf13d27]746            else:
[4f26bdb]747                return ('data', [lo])
748        return ('control', '%end', '%end')
[bf13d27]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,
[5c7b1ef]757                                               self.define('%{_arch}'),
758                                               self)
[bf13d27]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
[d963553b]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
[bf13d27]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,
[4f26bdb]787                                                   self.define('%{_arch}'),
788                                                   self)
[bf13d27]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        #
[d963553b]799        # Locate the config file. Expand any macros then add the
[bf13d27]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
[d963553b]808        #
809        # Macro could add an extension.
810        #
[bf13d27]811        if exname.endswith('.cfg'):
812            configname = exname
813        else:
814            configname = '%s.cfg' % (exname)
[d963553b]815            name = '%s.cfg' % (name)
[bf13d27]816
[d963553b]817        if ':' in configname:
818            cfgname = path.basename(configname)
819        else:
820            cfgname = common_end(configname, name)
[bf13d27]821
[ab8319a]822        if not path.exists(configname):
[bf13d27]823            if ':' in configname:
[ab8319a]824                configdirs = path.dirname(configname).split(':')
[bf13d27]825            else:
826                configdirs = self.define('_configdir').split(':')
827            for cp in configdirs:
[ab8319a]828                configname = path.join(path.abspath(cp), cfgname)
829                if path.exists(configname):
[bf13d27]830                    break
831                configname = None
832            if configname is None:
833                raise error.general('no config file found: %s' % (cfgname))
834
835        try:
836            if self.opts.trace():
[ab8319a]837                print '_open: %s' % (path.host(configname))
838            config = open(path.host(configname), 'r')
[bf13d27]839        except IOError, err:
[ab8319a]840            raise error.general('error opening config file: %s' % (path.host(configname)))
[bf13d27]841        self.configpath += [configname]
842
[79f80fd]843        self._includes += [configname]
844
[bf13d27]845        try:
846            dir = None
[3b0ce4e]847            info = None
[bf13d27]848            data = []
849            while True:
850                r = self._parse(config)
851                if r[0] == 'package':
852                    self._set_package(r[1])
853                    dir = None
854                elif r[0] == 'control':
855                    if r[1] == '%end':
856                        break
[50da39a]857                    self._warning("unexpected '%s'" % (r[1]))
[bf13d27]858                elif r[0] == 'directive':
859                    new_data = []
860                    if r[1] == '%description':
861                        new_data = [' '.join(r[2])]
862                    elif r[1] == '%include':
863                        self.load(r[2][0])
864                        continue
865                    else:
866                        if len(r[2]) == 0:
867                            _package = 'main'
868                        elif len(r[2]) == 1:
869                            _package = r[2][0]
870                        else:
871                            if r[2][0].strip() != '-n':
[ab8319a]872                                self._warning("unknown directive option: '%s'" % (' '.join(r[2])))
[bf13d27]873                            _package = r[2][1].strip()
874                        self._set_package(_package)
875                    if dir and dir != r[1]:
876                        self._directive_extend(dir, data)
877                    dir = r[1]
878                    data = new_data
879                elif r[0] == 'data':
880                    for l in r[1]:
881                        if l.startswith('%error'):
[4f26bdb]882                            l = self._expand(l)
[bf13d27]883                            raise error.general('config error: %s' % (l[7:]))
[5260449]884                        elif l.startswith('%warning'):
[4f26bdb]885                            l = self._expand(l)
[5260449]886                            print >> sys.stderr, 'warning: %s' % (l[9:])
887                            self._warning(l[9:])
[bf13d27]888                        if not dir:
[4f26bdb]889                            l = self._expand(l)
[bf13d27]890                            ls = self.tags.split(l, 1)
891                            if self.opts.trace():
892                                print '_tag: ', l, ls
893                            if len(ls) > 1:
[3b0ce4e]894                                info = ls[0].lower()
895                                if info[-1] == ':':
896                                    info = info[:-1]
897                                info_data = ls[1].strip()
898                            else:
899                                info_data = ls[0].strip()
900                            if info is not None:
901                                self._info_append(info, info_data)
[bf13d27]902                            else:
[50da39a]903                                self._warning("invalid format: '%s'" % (info_data[:-1]))
[bf13d27]904                        else:
905                            data.append(l)
906                else:
[50da39a]907                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
[bf13d27]908            if dir is not None:
909                self._directive_extend(dir, data)
910        except:
911            config.close()
912            raise
913
914        config.close()
915
916        self.name = save_name
917        self.lc = save_lc
918
919        self.load_depth -= 1
920
[4f26bdb]921    def defined(self, name):
[cb12e48]922        return self.macros.has_key(name)
[4f26bdb]923
[bf13d27]924    def define(self, name):
[cb12e48]925        if name in self.macros:
926            d = self.macros[name]
[bf13d27]927        else:
928            n = self._label(name)
[cb12e48]929            if n in self.macros:
930                d = self.macros[n]
[bf13d27]931            else:
[50da39a]932                raise error.general('%d: macro "%s" not found' % (self.lc, name))
[bf13d27]933        return self._expand(d)
934
[5260449]935    def set_define(self, name, value):
[376aabf]936        self.macros[name] = value
[5260449]937
[bf13d27]938    def expand(self, line):
[4f26bdb]939        if type(line) == list:
940            el = []
941            for l in line:
942                el += [self._expand(l)]
943            return el
[bf13d27]944        return self._expand(line)
945
[cb12e48]946    def macro(self, name):
[5c7b1ef]947        if name in self.macros:
948            return self.macros[name]
[4f26bdb]949        raise error.general('macro "%s" not found' % (name))
950
[bf13d27]951    def directive(self, _package, name):
952        if _package not in self._packages:
953            raise error.general('package "' + _package + '" not found')
954        if name not in self._packages[_package].directives:
955            raise error.general('directive "' + name + \
956                                    '" not found in package "' + _package + '"')
957        return self._packages[_package].directives[name]
958
[ab8319a]959    def abspath(self, rpath):
960        return path.abspath(self.define(rpath))
[bf13d27]961
962    def packages(self):
963        return self._packages
964
[79f80fd]965    def includes(self):
966        return self._includes
967
[bf13d27]968def run():
969    import sys
970    try:
[cb12e48]971        #
972        # Run where defaults.mc is located
973        #
974        opts = options.load(sys.argv, defaults = 'defaults.mc')
[bf13d27]975        if opts.trace():
976            print 'config: count %d' % (len(opts.config_files()))
977        for config_file in opts.config_files():
[cb12e48]978            s = file(config_file, opts)
[bf13d27]979            print s
980            del s
981    except error.general, gerr:
982        print gerr
983        sys.exit(1)
984    except error.internal, ierr:
985        print ierr
986        sys.exit(1)
987    except KeyboardInterrupt:
988        print 'user terminated'
989        sys.exit(1)
990    sys.exit(0)
991
992if __name__ == "__main__":
993    run()
Note: See TracBrowser for help on using the repository browser.