source: rtems-source-builder/source-builder/sb/config.py @ 845e92f

4.104.114.95
Last change on this file since 845e92f was 845e92f, checked in by Chris Johns <chrisj@…>, on 09/02/13 at 22:30:26

sb: Fix nesting if statements.

Nesting if statements did not correctly process the contents of
the nested if script. This change makes the processing uniform
across all the logic stages.

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