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

4.11
Last change on this file since cbcfcd0b was cbcfcd0b, checked in by Chris Johns <chrisj@…>, on 12/11/15 at 03:21:29

Remove the warning when undefining an undefined macro.

  • Property mode set to 100644
File size: 44.6 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
39    import pkgconfig
40    import sources
41except KeyboardInterrupt:
42    print 'user terminated'
43    sys.exit(1)
44except:
45    print 'error: unknown application load error'
46    sys.exit(1)
47
48def _check_bool(value):
49    if value.isdigit():
50        if int(value) == 0:
51            istrue = False
52        else:
53            istrue = True
54    else:
55        istrue = None
56    return istrue
57
58def _check_nil(value):
59    if len(value):
60        istrue = True
61    else:
62        istrue = False
63    return istrue
64
65class package:
66
67    def __init__(self, name, arch, config):
68        self._name = name
69        self._arch = arch
70        self.config = config
71        self.directives = {}
72        self.infos = {}
73
74    def __str__(self):
75
76        def _dictlist(dl):
77            s = ''
78            dll = dl.keys()
79            dll.sort()
80            for d in dll:
81                if d:
82                    s += '  ' + d + ':\n'
83                    for l in dl[d]:
84                        s += '    ' + l + '\n'
85            return s
86
87        s = '\npackage: ' + self._name + \
88            '\n directives:\n' + _dictlist(self.directives) + \
89            '\n infos:\n' + _dictlist(self.infos)
90
91        return s
92
93    def _macro_override(self, info, macro):
94        '''See if a macro overrides this setting.'''
95        overridden = self.config.macros.overridden(macro)
96        if overridden:
97            return self.config.macros.expand(macro)
98        return info
99
100    def directive_extend(self, dir, data):
101        if dir not in self.directives:
102            self.directives[dir] = []
103        for i in range(0, len(data)):
104            data[i] = data[i].strip()
105        self.directives[dir].extend(data)
106        self.config.macros[dir] = '\n'.join(self.directives[dir])
107
108    def info_append(self, info, data):
109        if info not in self.infos:
110            self.infos[info] = []
111        self.infos[info].append(data)
112        self.config.macros[info] = '\n'.join(self.infos[info])
113
114    def get_info(self, info, expand = True):
115        if info in self.config.macros:
116            _info = self.config.macros[info].split('\n')
117            if expand:
118                return self.config.expand(_info)
119            else:
120                return _info
121        return None
122
123    def extract_info(self, label, expand = True):
124        ll = label.lower()
125        infos = {}
126        keys = self.config.macros.find('%s.*' % (ll))
127        for k in keys:
128            if k == ll:
129                k = '%s0' % (ll)
130            elif not k[len(ll):].isdigit():
131                continue
132            infos[k] = [self.config.expand(self.config.macros[k])]
133        return infos
134
135    def _find_macro(self, label, expand = True):
136        if label in self.config.macros:
137            macro = self.config.macros[label].split('\n')
138            if expand:
139                return self.config.expand(macro)
140            else:
141                return macro
142        return None
143
144    def find_info(self, label, expand = True):
145        return self._find_macro(label, expand)
146
147    def find_directive(self, label, expand = True):
148        return self._find_macro(label, expand)
149
150    def name(self):
151        info = self.find_info('name')
152        if info:
153            n = info[0]
154        else:
155            n = self._name
156        return self._macro_override(n, 'name')
157
158    def summary(self):
159        info = self.find_info('summary')
160        if info:
161            return info[0]
162        return ''
163
164    def url(self):
165        info = self.find_info('url')
166        if info:
167            return info[0]
168        return ''
169
170    def version(self):
171        info = self.find_info('version')
172        if not info:
173            return None
174        return info[0]
175
176    def release(self):
177        info = self.find_info('release')
178        if not info:
179            return None
180        return info[0]
181
182    def buildarch(self):
183        info = self.find_info('buildarch')
184        if not info:
185            return self._arch
186        return info[0]
187
188    def sources(self):
189        return self.extract_info('source')
190
191    def patches(self):
192        return self.extract_info('patch')
193
194    def prep(self):
195        return self.find_directive('%prep')
196
197    def build(self):
198        return self.find_directive('%build')
199
200    def install(self):
201        return self.find_directive('%install')
202
203    def clean(self):
204        return self.find_directive('%clean')
205
206    def include(self):
207        return self.find_directive('%include')
208
209    def testing(self):
210        return self.find_directive('%testing')
211
212    def long_name(self):
213        return self.name()
214
215    def disabled(self):
216        return len(self.name()) == 0
217
218class file:
219    """Parse a config file."""
220
221    _directive = [ '%description',
222                   '%prep',
223                   '%build',
224                   '%clean',
225                   '%install',
226                   '%include',
227                   '%install',
228                   '%testing' ]
229
230    _ignore = [ re.compile('%setup'),
231                re.compile('%configure'),
232                re.compile('%source'),
233                re.compile('%patch'),
234                re.compile('%hash'),
235                re.compile('%select'),
236                re.compile('%disable') ]
237
238    def __init__(self, name, opts, macros = None):
239        log.trace('config: %s: initialising' % (name))
240        self.opts = opts
241        self.init_name = name
242        self.wss = re.compile(r'\s+')
243        self.tags = re.compile(r':+')
244        self.sf = re.compile(r'%\([^\)]+\)')
245        self.set_macros(macros)
246        self._reset(name)
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 _reset(self, name):
269        self.name = name
270        self.load_depth = 0
271        self.configpath = []
272        self._includes = []
273        self._packages = {}
274        self.in_error = False
275        self.lc = 0
276        self.conditionals = {}
277        self._packages = {}
278        self.package = 'main'
279        self.disable_macro_reassign = False
280        self.pkgconfig_prefix = None
281        self.pkgconfig_crosscompile = False
282        self.pkgconfig_filter_flags = False
283        for arg in self.opts.args:
284            if arg.startswith('--with-') or arg.startswith('--without-'):
285                if '=' in arg:
286                    label, value = arg.split('=', 1)
287                else:
288                    label = arg
289                    value = None
290                label = label[2:].lower().replace('-', '_')
291                if value:
292                    self.macros.define(label, value)
293                else:
294                    self.macros.define(label)
295
296    def _relative_path(self, p):
297        sbdir = None
298        if '_sbdir' in self.macros:
299            sbdir = path.dirname(self.expand('%{_sbdir}'))
300            if p.startswith(sbdir):
301                p = p[len(sbdir) + 1:]
302        return p
303
304    def _name_line_msg(self,  msg):
305        return '%s:%d: %s' % (path.basename(self.name), self.lc,  msg)
306
307    def _output(self, text):
308        if not self.opts.quiet():
309            log.output(text)
310
311    def _error(self, msg):
312        err = 'error: %s' % (self._name_line_msg(msg))
313        log.stderr(err)
314        log.output(err)
315        self.in_error = True
316        if not self.opts.dry_run():
317            log.stderr('warning: switched to dry run due to errors')
318            self.opts.set_dry_run()
319
320    def _label(self, name):
321        if name.startswith('%{') and name[-1] is '}':
322            return name
323        return '%{' + name.lower() + '}'
324
325    def _cross_compile(self):
326        _host = self.expand('%{_host}')
327        _build = self.expand('%{_build}')
328        return _host != _build
329
330    def _candian_cross_compile(self):
331        _host = self.expand('%{_host}')
332        _build = self.expand('%{_build}')
333        _target = self.expand('%{_target}')
334        _alloc_cxc = self.defined('%{allow_cxc}')
335        return _alloc_cxc and _host != _build and _host != _target
336
337    def _macro_split(self, s):
338        '''Split the string (s) up by macros. Only split on the
339           outter level. Nested levels will need to split with futher calls.'''
340        trace_me = False
341        if trace_me:
342            print '------------------------------------------------------'
343        macros = []
344        nesting = []
345        has_braces = False
346        c = 0
347        while c < len(s):
348            if trace_me:
349                print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting
350            #
351            # We need to watch for shell type variables or the form '${var}' because
352            # they can upset the brace matching.
353            #
354            if s[c] == '%' or s[c] == '$':
355                start = s[c]
356                c += 1
357                if c == len(s):
358                    continue
359                #
360                # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ?
361                #
362                if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'):
363                    continue
364                elif not s[c].isspace():
365                    #
366                    # If this is a shell macro and we are at the outter
367                    # level or is '$var' forget it and move on.
368                    #
369                    if start == '$' and (s[c] != '{' or len(nesting) == 0):
370                        continue
371                    if s[c] == '{':
372                        this_has_braces = True
373                    else:
374                        this_has_braces = False
375                    nesting.append((c - 1, has_braces))
376                    has_braces = this_has_braces
377            elif len(nesting) > 0:
378                if s[c] == '}' or (s[c].isspace() and not has_braces):
379                    #
380                    # Can have '%{?test: something %more}' where the
381                    # nested %more ends with the '}' which also ends
382                    # the outter macro.
383                    #
384                    if not has_braces:
385                        if s[c] == '}':
386                            macro_start, has_braces = nesting[len(nesting) - 1]
387                            nesting = nesting[:-1]
388                            if len(nesting) == 0:
389                                macros.append(s[macro_start:c].strip())
390                    if len(nesting) > 0:
391                        macro_start, has_braces = nesting[len(nesting) - 1]
392                        nesting = nesting[:-1]
393                        if len(nesting) == 0:
394                            macros.append(s[macro_start:c + 1].strip())
395            c += 1
396        if trace_me:
397            print 'ms:', macros
398        if trace_me:
399            print '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='
400        return macros
401
402    def _shell(self, line):
403        sl = self.sf.findall(line)
404        if len(sl):
405            e = execute.capture_execution()
406            for s in sl:
407                if options.host_windows:
408                    cmd = '%s -c "%s"' % (self.macros.expand('%{__sh}'), s[2:-1])
409                else:
410                    cmd = s[2:-1]
411                exit_code, proc, output = e.shell(cmd)
412                log.trace('shell-output: %d %s' % (exit_code, output))
413                if exit_code == 0:
414                    line = line.replace(s, output)
415                else:
416                    raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output))
417        return line
418
419    def _pkgconfig_check(self, test):
420        ok = False
421        if type(test) == str:
422            test = test.split()
423        if not self._cross_compile() or self.pkgconfig_crosscompile:
424            try:
425                pkg = pkgconfig.package(test[0],
426                                        prefix = self.pkgconfig_prefix,
427                                        output = self._output,
428                                        src = log.trace)
429                if len(test) != 1 and len(test) != 3:
430                    self._error('malformed check: %s' % (' '.join(test)))
431                else:
432                    op = '>='
433                    ver = '0'
434                    if len(test) == 3:
435                        op = test[1]
436                        ver = self.macros.expand(test[2])
437                    ok = pkg.check(op, ver)
438            except pkgconfig.error, pe:
439                self._error('pkgconfig: check: %s' % (pe))
440            except:
441                raise error.internal('pkgconfig failure')
442        if ok:
443            return '1'
444        return '0'
445
446    def _pkgconfig_flags(self, package, flags):
447        pkg_flags = None
448        if not self._cross_compile() or self.pkgconfig_crosscompile:
449            try:
450                pkg = pkgconfig.package(package,
451                                        prefix = self.pkgconfig_prefix,
452                                        output = self._output,
453                                        src = log.trace)
454                pkg_flags = pkg.get(flags)
455                if pkg_flags and self.pkgconfig_filter_flags:
456                    fflags = []
457                    for f in pkg_flags.split():
458                        if not f.startswith('-f') and not f.startswith('-W'):
459                            fflags += [f]
460                    pkg_flags = ' '.join(fflags)
461                log.trace('pkgconfig: %s: %s' % (flags, pkg_flags))
462            except pkgconfig.error, pe:
463                self._error('pkgconfig: %s: %s' % (flags, pe))
464            except:
465                raise error.internal('pkgconfig failure')
466        if pkg_flags is None:
467            pkg_flags = ''
468        return pkg_flags
469
470    def _pkgconfig(self, pcl):
471        ok = False
472        ps = ''
473        if pcl[0] == 'check':
474            ps = self._pkgconfig_check(pcl[1:])
475        elif pcl[0] == 'prefix':
476            if len(pcl) == 2:
477                self.pkgconfig_prefix = pcl[1]
478            else:
479                self._error('prefix error: %s' % (' '.join(pcl)))
480        elif pcl[0] == 'crosscompile':
481            ok = True
482            if len(pcl) == 2:
483                if pcl[1].lower() == 'yes':
484                    self.pkgconfig_crosscompile = True
485                elif pcl[1].lower() == 'no':
486                    self.pkgconfig_crosscompile = False
487                else:
488                    ok = False
489            else:
490                ok = False
491            if not ok:
492                self._error('crosscompile error: %s' % (' '.join(pcl)))
493        elif pcl[0] == 'filter-flags':
494            ok = True
495            if len(pcl) == 2:
496                if pcl[1].lower() == 'yes':
497                    self.pkgconfig_filter_flags = True
498                elif pcl[1].lower() == 'no':
499                    self.pkgconfig_filter_flags = False
500                else:
501                    ok = False
502            else:
503                ok = False
504            if not ok:
505                self._error('crosscompile error: %s' % (' '.join(pcl)))
506        elif pcl[0] in ['ccflags', 'cflags', 'ldflags', 'libs']:
507            ps = self._pkgconfig_flags(pcl[1], pcl[0])
508        else:
509            self._error('pkgconfig error: %s' % (' '.join(pcl)))
510        return ps
511
512    def _expand(self, s):
513        expand_count = 0
514        expanded = True
515        while expanded:
516            expand_count += 1
517            if expand_count > 500:
518                raise error.general('macro expand looping: %s' % (s))
519            expanded = False
520            ms = self._macro_split(s)
521            for m in ms:
522                mn = m
523                #
524                # A macro can be '%{macro}' or '%macro'. Turn the later into
525                # the former.
526                #
527                show_warning = True
528                if mn[1] != '{':
529                    for r in self._ignore:
530                        if r.match(mn) is not None:
531                            mn = None
532                            break
533                    else:
534                        mn = self._label(mn[1:])
535                        show_warning = False
536                elif m.startswith('%{expand'):
537                    colon = m.find(':')
538                    if colon < 8:
539                        log.warning('malformed expand macro, no colon found')
540                    else:
541                        e = self._expand(m[colon + 1:-1].strip())
542                        s = s.replace(m, self._label(e))
543                        expanded = True
544                        mn = None
545                elif m.startswith('%{with '):
546                    #
547                    # Change the ' ' to '_' because the macros have no spaces.
548                    #
549                    n = self._label('with_' + m[7:-1].strip())
550                    if n in self.macros:
551                        s = s.replace(m, '1')
552                    else:
553                        s = s.replace(m, '0')
554                    expanded = True
555                    mn = None
556                elif m.startswith('%{echo'):
557                    if not m.endswith('}'):
558                        log.warning("malformed conditional macro '%s'" % (m))
559                        mn = None
560                    else:
561                        e = self._expand(m[6:-1].strip())
562                        log.notice('%s' % (self._name_line_msg(e)))
563                        s = ''
564                        expanded = True
565                        mn = None
566                elif m.startswith('%{defined'):
567                    n = self._label(m[9:-1].strip())
568                    if n in self.macros:
569                        s = s.replace(m, '1')
570                    else:
571                        s = s.replace(m, '0')
572                    expanded = True
573                    mn = None
574                elif m.startswith('%{path '):
575                    pl = m[7:-1].strip().split()
576                    ok = False
577                    if len(pl) == 2:
578                        ok = True
579                        epl = []
580                        for p in pl[1:]:
581                            epl += [self._expand(p)]
582                        p = ' '.join(epl)
583                        if pl[0].lower() == 'prepend':
584                            if len(self.macros['_pathprepend']):
585                                self.macros['_pathprepend'] = \
586                                    '%s:%s' % (p, self.macros['_pathprepend'])
587                            else:
588                                self.macros['_pathprepend'] = p
589                        elif pl[0].lower() == 'postpend':
590                            if len(self.macros['_pathprepend']):
591                                self.macros['_pathprepend'] = \
592                                    '%s:%s' % (self.macros['_pathprepend'], p)
593                            else:
594                                self.macros['_pathprepend'] = p
595                        else:
596                            ok = False
597                    if ok:
598                        s = s.replace(m, '')
599                    else:
600                        self._error('path error: %s' % (' '.join(pl)))
601                    mn = None
602                elif m.startswith('%{pkgconfig '):
603                    pcl = m[11:-1].strip().split()
604                    if len(pcl):
605                        epcl = []
606                        for pc in pcl:
607                            epcl += [self._expand(pc)]
608                        ps = self._pkgconfig(epcl)
609                        s = s.replace(m, ps)
610                        expanded = True
611                    else:
612                        self._error('pkgconfig error: %s' % (m[11:-1].strip()))
613                    mn = None
614                elif m.startswith('%{?') or m.startswith('%{!?'):
615                    if m[2] == '!':
616                        start = 4
617                    else:
618                        start = 3
619                    colon = m[start:].find(':')
620                    if colon < 0:
621                        if not m.endswith('}'):
622                            log.warning("malformed conditional macro '%s'" % (m))
623                            mn = None
624                        else:
625                            mn = self._label(m[start:-1])
626                    else:
627                        mn = self._label(m[start:start + colon])
628                    if mn:
629                        if m.startswith('%{?'):
630                            istrue = False
631                            if mn in self.macros:
632                                # If defined and 0 or '' then it is false.
633                                istrue = _check_bool(self.macros[mn])
634                                if istrue is None:
635                                    istrue = _check_nil(self.macros[mn])
636                            if colon >= 0 and istrue:
637                                s = s.replace(m, m[start + colon + 1:-1])
638                                expanded = True
639                                mn = None
640                            elif not istrue:
641                                mn = '%{nil}'
642                        else:
643                            isfalse = True
644                            if mn in self.macros:
645                                istrue = _check_bool(self.macros[mn])
646                                if istrue is None or istrue == True:
647                                    isfalse = False
648                            if colon >= 0 and isfalse:
649                                s = s.replace(m, m[start + colon + 1:-1])
650                                expanded = True
651                                mn = None
652                            else:
653                                mn = '%{nil}'
654                if mn:
655                    if mn.lower() in self.macros:
656                        s = s.replace(m, self.macros[mn.lower()])
657                        expanded = True
658                    elif show_warning:
659                        self._error("macro '%s' not found" % (mn))
660        return self._shell(s)
661
662    def _disable(self, config, ls):
663        if len(ls) != 2:
664            log.warning('invalid disable statement')
665        else:
666            if ls[1] == 'select':
667                self.macros.lock_read_map()
668                log.trace('config: %s: _disable_select: %s' % (self.name, ls[1]))
669            else:
670                log.warning('invalid disable statement: %s' % (ls[1]))
671
672    def _select(self, config, ls):
673        if len(ls) != 2:
674            log.warning('invalid select statement')
675        else:
676            r = self.macros.set_read_map(ls[1])
677            log.trace('config: %s: _select: %s %s %r' % \
678                          (self.name, r, ls[1], self.macros.maps()))
679
680    def _sources(self, ls):
681        return sources.process(ls[0][1:], ls[1:], self.macros, self._error)
682
683    def _hash(self, ls):
684        return sources.hash(ls[1:], self.macros, self._error)
685
686    def _define(self, config, ls):
687        if len(ls) <= 1:
688            log.warning('invalid macro definition')
689        else:
690            d = self._label(ls[1])
691            if self.disable_macro_reassign:
692                if (d not in self.macros) or \
693                        (d in self.macros and len(self.macros[d]) == 0):
694                    if len(ls) == 2:
695                        self.macros[d] = '1'
696                    else:
697                        self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
698                else:
699                    log.warning("macro '%s' already defined" % (d))
700            else:
701                if len(ls) == 2:
702                    self.macros[d] = '1'
703                else:
704                    self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
705
706    def _undefine(self, config, ls):
707        if len(ls) <= 1:
708            log.warning('invalid macro definition')
709        else:
710            mn = self._label(ls[1])
711            if mn in self.macros:
712                del self.macros[mn]
713
714    def _ifs(self, config, ls, label, iftrue, isvalid, dir, info):
715        in_iftrue = True
716        data = []
717        while True:
718            if isvalid and \
719                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
720                this_isvalid = True
721            else:
722                this_isvalid = False
723            r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid)
724            if r[0] == 'package':
725                if this_isvalid:
726                    dir, info, data = self._process_package(r, dir, info, data)
727            elif r[0] == 'control':
728                if r[1] == '%end':
729                    self._error(label + ' without %endif')
730                    raise error.general('terminating build')
731                if r[1] == '%endif':
732                    log.trace('config: %s: _ifs: %s %s' % (self.name, r[1], this_isvalid))
733                    return data
734                if r[1] == '%else':
735                    in_iftrue = False
736            elif r[0] == 'directive':
737                if this_isvalid:
738                    if r[1] == '%include':
739                        self.load(r[2][0])
740                        continue
741                    dir, info, data = self._process_directive(r, dir, info, data)
742            elif r[0] == 'data':
743                if this_isvalid:
744                    dir, info, data = self._process_data(r, dir, info, data)
745        # @note is a directive extend missing
746
747    def _if(self, config, ls, isvalid, dir, info, invert = False):
748
749        def add(x, y):
750            return x + ' ' + str(y)
751
752        istrue = False
753        if isvalid:
754            if len(ls) == 2:
755                s = ls[1]
756            else:
757                s = (ls[1] + ' ' + ls[2])
758            ifls = s.split()
759            if len(ifls) == 1:
760                #
761                # Check if '%if %{x} == %{nil}' has both parts as nothing
762                # which means '%if ==' is always True and '%if !=' is always false.
763                #
764                if ifls[0] == '==':
765                    istrue = True
766                elif ifls[0] == '!=':
767                    istrue = False
768                else:
769                    istrue = _check_bool(ifls[0])
770                    if istrue == None:
771                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
772                        istrue = False
773            elif len(ifls) == 2:
774                if ifls[0] == '!':
775                    istrue = _check_bool(ifls[1])
776                    if istrue == None:
777                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
778                        istrue = False
779                    else:
780                        istrue = not istrue
781                else:
782                    #
783                    # Check is something is being checked against empty,
784                    #   ie '%if %{x} == %{nil}'
785                    # The logic is 'something == nothing' is False and
786                    # 'something != nothing' is True.
787                    #
788                    if ifls[1] == '==':
789                        istrue = False
790                    elif  ifls[1] == '!=':
791                        istrue = True
792                    else:
793                        self._error('invalid if bool operator: ' + reduce(add, ls, ''))
794            else:
795                if len(ifls) > 3:
796                    for op in ['==', '!=', '>=', '=>', '=<', '<=', '>', '<']:
797                        ops = s.split(op)
798                        if len(ops) == 2:
799                            ifls = (ops[0], op, ops[1])
800                            break
801                if len(ifls) != 3:
802                     self._error('malformed if: ' + reduce(add, ls, ''))
803                if ifls[1] == '==':
804                    if ifls[0] == ifls[2]:
805                        istrue = True
806                    else:
807                        istrue = False
808                elif ifls[1] == '!=' or ifls[1] == '=!':
809                    if ifls[0] != ifls[2]:
810                        istrue = True
811                    else:
812                        istrue = False
813                elif ifls[1] == '>':
814                    if ifls[0] > ifls[2]:
815                        istrue = True
816                    else:
817                        istrue = False
818                elif ifls[1] == '>=' or ifls[1] == '=>':
819                    if ifls[0] >= ifls[2]:
820                        istrue = True
821                    else:
822                        istrue = False
823                elif ifls[1] == '<=' or ifls[1] == '=<':
824                    if ifls[0] <= ifls[2]:
825                        istrue = True
826                    else:
827                        istrue = False
828                elif ifls[1] == '<':
829                    if ifls[0] < ifls[2]:
830                        istrue = True
831                    else:
832                        istrue = False
833                else:
834                    self._error('invalid %if operator: ' + reduce(add, ls, ''))
835            if invert:
836                istrue = not istrue
837            log.trace('config: %s: _if:  %s %s' % (self.name, ifls, str(istrue)))
838        return self._ifs(config, ls, '%if', istrue, isvalid, dir, info)
839
840    def _ifos(self, config, ls, isvalid, dir, info):
841        isos = False
842        if isvalid:
843            os = self.define('_os')
844            for l in ls:
845                if l in os:
846                    isos = True
847                    break
848        return self._ifs(config, ls, '%ifos', isos, isvalid, dir, info)
849
850    def _ifarch(self, config, positive, ls, isvalid, dir, info):
851        isarch = False
852        if isvalid:
853            arch = self.define('_arch')
854            for l in ls:
855                if l in arch:
856                    isarch = True
857                    break
858        if not positive:
859            isarch = not isarch
860        return self._ifs(config, ls, '%ifarch', isarch, isvalid, dir, info)
861
862    def _parse(self, config, dir, info, roc = False, isvalid = True):
863        # roc = return on control
864
865        def _clean(line):
866            line = line[0:-1]
867            b = line.find('#')
868            if b >= 0:
869                line = line[1:b]
870            return line.strip()
871
872        #
873        # Need to add code to count matching '{' and '}' and if they
874        # do not match get the next line and add to the string until
875        # they match. This closes an opening '{' that is on another
876        # line.
877        #
878        for l in config:
879            self.lc += 1
880            l = _clean(l)
881            if len(l) == 0:
882                continue
883            log.trace('config: %s: %03d: %s %s' % \
884                          (self.name, self.lc, str(isvalid), l))
885            lo = l
886            if isvalid:
887                l = self._expand(l)
888            if len(l) == 0:
889                continue
890            if l[0] == '%':
891                ls = self.wss.split(l, 2)
892                los = self.wss.split(lo, 2)
893                if ls[0] == '%package':
894                    if isvalid:
895                        if ls[1] == '-n':
896                            name = ls[2]
897                        else:
898                            name = self.name + '-' + ls[1]
899                        return ('package', name)
900                elif ls[0] == '%disable':
901                    if isvalid:
902                        self._disable(config, ls)
903                elif ls[0] == '%select':
904                    if isvalid:
905                        self._select(config, ls)
906                elif ls[0] == '%source' or ls[0] == '%patch':
907                    if isvalid:
908                        d = self._sources(ls)
909                        if d is not None:
910                            return ('data', d)
911                elif ls[0] == '%hash':
912                    if isvalid:
913                        d = self._hash(ls)
914                        if d is not None:
915                            return ('data', d)
916                elif ls[0] == '%patch':
917                    if isvalid:
918                        self._select(config, ls)
919                elif ls[0] == '%error':
920                    if isvalid:
921                        return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))])
922                elif ls[0] == '%warning':
923                    if isvalid:
924                        return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))])
925                elif ls[0] == '%define' or ls[0] == '%global':
926                    if isvalid:
927                        self._define(config, ls)
928                elif ls[0] == '%undefine':
929                    if isvalid:
930                        self._undefine(config, ls)
931                elif ls[0] == '%if':
932                    d = self._if(config, ls, isvalid, dir, info)
933                    if len(d):
934                        log.trace('config: %s: %%if: %s' % (self.name, d))
935                        return ('data', d)
936                elif ls[0] == '%ifn':
937                    d = self._if(config, ls, isvalid, dir, info, True)
938                    if len(d):
939                        log.trace('config: %s: %%ifn: %s' % (self.name, d))
940                        return ('data', d)
941                elif ls[0] == '%ifos':
942                    d = self._ifos(config, ls, isvalid, dir, info)
943                    if len(d):
944                        return ('data', d)
945                elif ls[0] == '%ifarch':
946                    d = self._ifarch(config, True, ls, isvalid, dir, info)
947                    if len(d):
948                        return ('data', d)
949                elif ls[0] == '%ifnarch':
950                    d = self._ifarch(config, False, ls, isvalid, dir, info)
951                    if len(d):
952                        return ('data', d)
953                elif ls[0] == '%endif':
954                    if roc:
955                        return ('control', '%endif', '%endif')
956                    log.warning("unexpected '" + ls[0] + "'")
957                elif ls[0] == '%else':
958                    if roc:
959                        return ('control', '%else', '%else')
960                    log.warning("unexpected '" + ls[0] + "'")
961                elif ls[0].startswith('%defattr'):
962                    return ('data', [l])
963                elif ls[0] == '%bcond_with':
964                    if isvalid:
965                        #
966                        # Check if already defined. Would be by the command line or
967                        # even a host specific default.
968                        #
969                        if self._label('with_' + ls[1]) not in self.macros:
970                            self._define(config, (ls[0], 'without_' + ls[1]))
971                elif ls[0] == '%bcond_without':
972                    if isvalid:
973                        if self._label('without_' + ls[1]) not in self.macros:
974                            self._define(config, (ls[0], 'with_' + ls[1]))
975                else:
976                    for r in self._ignore:
977                        if r.match(ls[0]) is not None:
978                            return ('data', [l])
979                    if isvalid:
980                        for d in self._directive:
981                            if ls[0].strip() == d:
982                                return ('directive', ls[0].strip(), ls[1:])
983                        log.warning("unknown directive: '" + ls[0] + "'")
984                        return ('data', [lo])
985            else:
986                return ('data', [lo])
987        return ('control', '%end', '%end')
988
989    def _process_package(self, results, directive, info, data):
990        self._set_package(results[1])
991        directive = None
992        return (directive, info, data)
993
994    def _process_directive(self, results, directive, info, data):
995        new_data = []
996        if results[1] == '%description':
997            new_data = [' '.join(results[2])]
998            if len(results[2]) == 0:
999                _package = 'main'
1000            elif len(results[2]) == 1:
1001                _package = results[2][0]
1002            else:
1003                if results[2][0].strip() != '-n':
1004                    log.warning("unknown directive option: '%s'" % (' '.join(results[2])))
1005                _package = results[2][1].strip()
1006            self._set_package(_package)
1007        if directive and directive != results[1]:
1008            self._directive_extend(directive, data)
1009        directive = results[1]
1010        data = new_data
1011        return (directive, info, data)
1012
1013    def _process_data(self, results, directive, info, data):
1014        new_data = []
1015        for l in results[1]:
1016            if l.startswith('%error'):
1017                l = self._expand(l)
1018                raise error.general('config error: %s' % (l[7:]))
1019            elif l.startswith('%warning'):
1020                l = self._expand(l)
1021                log.stderr('warning: %s' % (l[9:]))
1022                log.warning(l[9:])
1023            if not directive:
1024                l = self._expand(l)
1025                ls = self.tags.split(l, 1)
1026                log.trace('config: %s: _tag: %s %s' % (self.name, l, ls))
1027                if len(ls) > 1:
1028                    info = ls[0].lower()
1029                    if info[-1] == ':':
1030                        info = info[:-1]
1031                    info_data = ls[1].strip()
1032                else:
1033                    info_data = ls[0].strip()
1034                if info is not None:
1035                    self._info_append(info, info_data)
1036                else:
1037                    log.warning("invalid format: '%s'" % (info_data[:-1]))
1038            else:
1039                l = self._expand(l)
1040                log.trace('config: %s: _data: %s %s' % (self.name, l, new_data))
1041                new_data.append(l)
1042        return (directive, info, data + new_data)
1043
1044    def _set_package(self, _package):
1045        if self.package == 'main' and \
1046                self._packages[self.package].name() != None:
1047            if self._packages[self.package].name() == _package:
1048                return
1049        if _package not in self._packages:
1050            self._packages[_package] = package(_package,
1051                                               self.define('%{_arch}'),
1052                                               self)
1053        self.package = _package
1054
1055    def _directive_extend(self, dir, data):
1056        self._packages[self.package].directive_extend(dir, data)
1057
1058    def _info_append(self, info, data):
1059        self._packages[self.package].info_append(info, data)
1060
1061    def set_macros(self, macros):
1062        if macros is None:
1063            self.macros = opts.defaults
1064        else:
1065            self.macros = macros
1066
1067    def load(self, name):
1068
1069        def common_end(left, right):
1070            end = ''
1071            while len(left) and len(right):
1072                if left[-1] != right[-1]:
1073                    return end
1074                end = left[-1] + end
1075                left = left[:-1]
1076                right = right[:-1]
1077            return end
1078
1079        if self.load_depth == 0:
1080            self._reset(name)
1081            self._packages[self.package] = package(self.package,
1082                                                   self.define('%{_arch}'),
1083                                                   self)
1084
1085        self.load_depth += 1
1086
1087        save_name = self.name
1088        save_lc = self.lc
1089
1090        #
1091        # Locate the config file. Expand any macros then add the
1092        # extension. Check if the file exists, therefore directly
1093        # referenced. If not see if the file contains ':' or the path
1094        # separator. If it does split the path else use the standard config dir
1095        # path in the defaults.
1096        #
1097
1098        exname = self.expand(name)
1099
1100        #
1101        # Macro could add an extension.
1102        #
1103        if exname.endswith('.cfg'):
1104            configname = exname
1105        else:
1106            configname = '%s.cfg' % (exname)
1107            name = '%s.cfg' % (name)
1108
1109        if ':' in configname:
1110            cfgname = path.basename(configname)
1111        else:
1112            cfgname = common_end(configname, name)
1113
1114        if not path.exists(configname):
1115            if ':' in configname:
1116                configdirs = path.dirname(configname).split(':')
1117            else:
1118                configdirs = self.define('_configdir').split(':')
1119            for cp in configdirs:
1120                configname = path.join(path.abspath(cp), cfgname)
1121                if path.exists(configname):
1122                    break
1123                configname = None
1124            if configname is None:
1125                raise error.general('no config file found: %s' % (cfgname))
1126
1127        try:
1128            log.trace('config: %s: _open: %s' % (self.name, path.host(configname)))
1129            config = open(path.host(configname), 'r')
1130        except IOError, err:
1131            raise error.general('error opening config file: %s' % (path.host(configname)))
1132
1133        self.configpath += [configname]
1134        self._includes += [configname]
1135
1136        self.name = self._relative_path(configname)
1137        self.lc = 0
1138
1139        try:
1140            dir = None
1141            info = None
1142            data = []
1143            while True:
1144                r = self._parse(config, dir, info)
1145                if r[0] == 'package':
1146                    dir, info, data = self._process_package(r, dir, info, data)
1147                elif r[0] == 'control':
1148                    if r[1] == '%end':
1149                        break
1150                    log.warning("unexpected '%s'" % (r[1]))
1151                elif r[0] == 'directive':
1152                    if r[1] == '%include':
1153                        self.load(r[2][0])
1154                        continue
1155                    dir, info, data = self._process_directive(r, dir, info, data)
1156                elif r[0] == 'data':
1157                    dir, info, data = self._process_data(r, dir, info, data)
1158                else:
1159                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
1160            if dir is not None:
1161                self._directive_extend(dir, data)
1162        except:
1163            config.close()
1164            raise
1165
1166        config.close()
1167
1168        self.name = save_name
1169        self.lc = save_lc
1170
1171        self.load_depth -= 1
1172
1173    def defined(self, name):
1174        return self.macros.has_key(name)
1175
1176    def define(self, name):
1177        if name in self.macros:
1178            d = self.macros[name]
1179        else:
1180            n = self._label(name)
1181            if n in self.macros:
1182                d = self.macros[n]
1183            else:
1184                raise error.general('%d: macro "%s" not found' % (self.lc, name))
1185        return self._expand(d)
1186
1187    def set_define(self, name, value):
1188        self.macros[name] = value
1189
1190    def expand(self, line):
1191        if type(line) == list:
1192            el = []
1193            for l in line:
1194                el += [self._expand(l)]
1195            return el
1196        return self._expand(line)
1197
1198    def macro(self, name):
1199        if name in self.macros:
1200            return self.macros[name]
1201        raise error.general('macro "%s" not found' % (name))
1202
1203    def directive(self, _package, name):
1204        if _package not in self._packages:
1205            raise error.general('package "' + _package + '" not found')
1206        if name not in self._packages[_package].directives:
1207            raise error.general('directive "' + name + \
1208                                    '" not found in package "' + _package + '"')
1209        return self._packages[_package].directives[name]
1210
1211    def abspath(self, rpath):
1212        return path.abspath(self.define(rpath))
1213
1214    def packages(self):
1215        return self._packages
1216
1217    def includes(self):
1218        return self._includes
1219
1220    def file_name(self):
1221        return self.name
1222
1223def run():
1224    import sys
1225    try:
1226        #
1227        # Run where defaults.mc is located
1228        #
1229        opts = options.load(sys.argv, defaults = 'defaults.mc')
1230        log.trace('config: count %d' % (len(opts.config_files())))
1231        for config_file in opts.config_files():
1232            s = file(config_file, opts)
1233            print s
1234            del s
1235    except error.general, gerr:
1236        print gerr
1237        sys.exit(1)
1238    except error.internal, ierr:
1239        print ierr
1240        sys.exit(1)
1241    except KeyboardInterrupt:
1242        log.notice('abort: user terminated')
1243        sys.exit(1)
1244    sys.exit(0)
1245
1246if __name__ == "__main__":
1247    run()
Note: See TracBrowser for help on using the repository browser.