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

4.104.114.95
Last change on this file since 9976801 was 9976801, checked in by Chris Johns <chrisj@…>, on Jun 5, 2015 at 6:58:29 AM

sb: Expand each data line.

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