source: rtems-source-builder/source-builder/sb/config.py @ 6444d58

4.104.95
Last change on this file since 6444d58 was 6444d58, checked in by Chris Johns <chrisj@…>, on Jul 20, 2015 at 3:49:42 AM

Canandian Cross Compiling and RTEMS 3rd party package building Fixes.

The change fixes installing for RTEMS 3rd Party packages where the
RSB considered them Canadian Cross Compiling (Cxc). Fixing the
Cxc issue broke real Cxc builds. The change corrects the issue of
macros being changed in the Cxc and the prep data not being udpated.
The configuration is loaded again after the updated macros. The
macros are also copied and restored to ensure a clean stable base.

The change also introduces --rtems-tools and --rtems-bsp to align
the command line with the waf configure process or RTEMS application.

  • 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            else:
714                log.warning("macro '%s' not defined" % (mn))
715
716    def _ifs(self, config, ls, label, iftrue, isvalid, dir, info):
717        in_iftrue = True
718        data = []
719        while True:
720            if isvalid and \
721                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
722                this_isvalid = True
723            else:
724                this_isvalid = False
725            r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid)
726            if r[0] == 'package':
727                if this_isvalid:
728                    dir, info, data = self._process_package(r, dir, info, data)
729            elif r[0] == 'control':
730                if r[1] == '%end':
731                    self._error(label + ' without %endif')
732                    raise error.general('terminating build')
733                if r[1] == '%endif':
734                    log.trace('config: %s: _ifs: %s %s' % (self.name, r[1], this_isvalid))
735                    return data
736                if r[1] == '%else':
737                    in_iftrue = False
738            elif r[0] == 'directive':
739                if this_isvalid:
740                    if r[1] == '%include':
741                        self.load(r[2][0])
742                        continue
743                    dir, info, data = self._process_directive(r, dir, info, data)
744            elif r[0] == 'data':
745                if this_isvalid:
746                    dir, info, data = self._process_data(r, dir, info, data)
747        # @note is a directive extend missing
748
749    def _if(self, config, ls, isvalid, dir, info, invert = False):
750
751        def add(x, y):
752            return x + ' ' + str(y)
753
754        istrue = False
755        if isvalid:
756            if len(ls) == 2:
757                s = ls[1]
758            else:
759                s = (ls[1] + ' ' + ls[2])
760            ifls = s.split()
761            if len(ifls) == 1:
762                #
763                # Check if '%if %{x} == %{nil}' has both parts as nothing
764                # which means '%if ==' is always True and '%if !=' is always false.
765                #
766                if ifls[0] == '==':
767                    istrue = True
768                elif ifls[0] == '!=':
769                    istrue = False
770                else:
771                    istrue = _check_bool(ifls[0])
772                    if istrue == None:
773                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
774                        istrue = False
775            elif len(ifls) == 2:
776                if ifls[0] == '!':
777                    istrue = _check_bool(ifls[1])
778                    if istrue == None:
779                        self._error('invalid if bool value: ' + reduce(add, ls, ''))
780                        istrue = False
781                    else:
782                        istrue = not istrue
783                else:
784                    #
785                    # Check is something is being checked against empty,
786                    #   ie '%if %{x} == %{nil}'
787                    # The logic is 'something == nothing' is False and
788                    # 'something != nothing' is True.
789                    #
790                    if ifls[1] == '==':
791                        istrue = False
792                    elif  ifls[1] == '!=':
793                        istrue = True
794                    else:
795                        self._error('invalid if bool operator: ' + reduce(add, ls, ''))
796            else:
797                if len(ifls) > 3:
798                    for op in ['==', '!=', '>=', '=>', '=<', '<=', '>', '<']:
799                        ops = s.split(op)
800                        if len(ops) == 2:
801                            ifls = (ops[0], op, ops[1])
802                            break
803                if len(ifls) != 3:
804                     self._error('malformed if: ' + reduce(add, ls, ''))
805                if ifls[1] == '==':
806                    if ifls[0] == ifls[2]:
807                        istrue = True
808                    else:
809                        istrue = False
810                elif ifls[1] == '!=' or ifls[1] == '=!':
811                    if ifls[0] != ifls[2]:
812                        istrue = True
813                    else:
814                        istrue = False
815                elif ifls[1] == '>':
816                    if ifls[0] > ifls[2]:
817                        istrue = True
818                    else:
819                        istrue = False
820                elif ifls[1] == '>=' or ifls[1] == '=>':
821                    if ifls[0] >= ifls[2]:
822                        istrue = True
823                    else:
824                        istrue = False
825                elif ifls[1] == '<=' or ifls[1] == '=<':
826                    if ifls[0] <= ifls[2]:
827                        istrue = True
828                    else:
829                        istrue = False
830                elif ifls[1] == '<':
831                    if ifls[0] < ifls[2]:
832                        istrue = True
833                    else:
834                        istrue = False
835                else:
836                    self._error('invalid %if operator: ' + reduce(add, ls, ''))
837            if invert:
838                istrue = not istrue
839            log.trace('config: %s: _if:  %s %s' % (self.name, ifls, str(istrue)))
840        return self._ifs(config, ls, '%if', istrue, isvalid, dir, info)
841
842    def _ifos(self, config, ls, isvalid, dir, info):
843        isos = False
844        if isvalid:
845            os = self.define('_os')
846            for l in ls:
847                if l in os:
848                    isos = True
849                    break
850        return self._ifs(config, ls, '%ifos', isos, isvalid, dir, info)
851
852    def _ifarch(self, config, positive, ls, isvalid, dir, info):
853        isarch = False
854        if isvalid:
855            arch = self.define('_arch')
856            for l in ls:
857                if l in arch:
858                    isarch = True
859                    break
860        if not positive:
861            isarch = not isarch
862        return self._ifs(config, ls, '%ifarch', isarch, isvalid, dir, info)
863
864    def _parse(self, config, dir, info, roc = False, isvalid = True):
865        # roc = return on control
866
867        def _clean(line):
868            line = line[0:-1]
869            b = line.find('#')
870            if b >= 0:
871                line = line[1:b]
872            return line.strip()
873
874        #
875        # Need to add code to count matching '{' and '}' and if they
876        # do not match get the next line and add to the string until
877        # they match. This closes an opening '{' that is on another
878        # line.
879        #
880        for l in config:
881            self.lc += 1
882            l = _clean(l)
883            if len(l) == 0:
884                continue
885            log.trace('config: %s: %03d: %s %s' % \
886                          (self.name, self.lc, str(isvalid), l))
887            lo = l
888            if isvalid:
889                l = self._expand(l)
890            if len(l) == 0:
891                continue
892            if l[0] == '%':
893                ls = self.wss.split(l, 2)
894                los = self.wss.split(lo, 2)
895                if ls[0] == '%package':
896                    if isvalid:
897                        if ls[1] == '-n':
898                            name = ls[2]
899                        else:
900                            name = self.name + '-' + ls[1]
901                        return ('package', name)
902                elif ls[0] == '%disable':
903                    if isvalid:
904                        self._disable(config, ls)
905                elif ls[0] == '%select':
906                    if isvalid:
907                        self._select(config, ls)
908                elif ls[0] == '%source' or ls[0] == '%patch':
909                    if isvalid:
910                        d = self._sources(ls)
911                        if d is not None:
912                            return ('data', d)
913                elif ls[0] == '%hash':
914                    if isvalid:
915                        d = self._hash(ls)
916                        if d is not None:
917                            return ('data', d)
918                elif ls[0] == '%patch':
919                    if isvalid:
920                        self._select(config, ls)
921                elif ls[0] == '%error':
922                    if isvalid:
923                        return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))])
924                elif ls[0] == '%warning':
925                    if isvalid:
926                        return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))])
927                elif ls[0] == '%define' or ls[0] == '%global':
928                    if isvalid:
929                        self._define(config, ls)
930                elif ls[0] == '%undefine':
931                    if isvalid:
932                        self._undefine(config, ls)
933                elif ls[0] == '%if':
934                    d = self._if(config, ls, isvalid, dir, info)
935                    if len(d):
936                        log.trace('config: %s: %%if: %s' % (self.name, d))
937                        return ('data', d)
938                elif ls[0] == '%ifn':
939                    d = self._if(config, ls, isvalid, dir, info, True)
940                    if len(d):
941                        log.trace('config: %s: %%ifn: %s' % (self.name, d))
942                        return ('data', d)
943                elif ls[0] == '%ifos':
944                    d = self._ifos(config, ls, isvalid, dir, info)
945                    if len(d):
946                        return ('data', d)
947                elif ls[0] == '%ifarch':
948                    d = self._ifarch(config, True, ls, isvalid, dir, info)
949                    if len(d):
950                        return ('data', d)
951                elif ls[0] == '%ifnarch':
952                    d = self._ifarch(config, False, ls, isvalid, dir, info)
953                    if len(d):
954                        return ('data', d)
955                elif ls[0] == '%endif':
956                    if roc:
957                        return ('control', '%endif', '%endif')
958                    log.warning("unexpected '" + ls[0] + "'")
959                elif ls[0] == '%else':
960                    if roc:
961                        return ('control', '%else', '%else')
962                    log.warning("unexpected '" + ls[0] + "'")
963                elif ls[0].startswith('%defattr'):
964                    return ('data', [l])
965                elif ls[0] == '%bcond_with':
966                    if isvalid:
967                        #
968                        # Check if already defined. Would be by the command line or
969                        # even a host specific default.
970                        #
971                        if self._label('with_' + ls[1]) not in self.macros:
972                            self._define(config, (ls[0], 'without_' + ls[1]))
973                elif ls[0] == '%bcond_without':
974                    if isvalid:
975                        if self._label('without_' + ls[1]) not in self.macros:
976                            self._define(config, (ls[0], 'with_' + ls[1]))
977                else:
978                    for r in self._ignore:
979                        if r.match(ls[0]) is not None:
980                            return ('data', [l])
981                    if isvalid:
982                        for d in self._directive:
983                            if ls[0].strip() == d:
984                                return ('directive', ls[0].strip(), ls[1:])
985                        log.warning("unknown directive: '" + ls[0] + "'")
986                        return ('data', [lo])
987            else:
988                return ('data', [lo])
989        return ('control', '%end', '%end')
990
991    def _process_package(self, results, directive, info, data):
992        self._set_package(results[1])
993        directive = None
994        return (directive, info, data)
995
996    def _process_directive(self, results, directive, info, data):
997        new_data = []
998        if results[1] == '%description':
999            new_data = [' '.join(results[2])]
1000            if len(results[2]) == 0:
1001                _package = 'main'
1002            elif len(results[2]) == 1:
1003                _package = results[2][0]
1004            else:
1005                if results[2][0].strip() != '-n':
1006                    log.warning("unknown directive option: '%s'" % (' '.join(results[2])))
1007                _package = results[2][1].strip()
1008            self._set_package(_package)
1009        if directive and directive != results[1]:
1010            self._directive_extend(directive, data)
1011        directive = results[1]
1012        data = new_data
1013        return (directive, info, data)
1014
1015    def _process_data(self, results, directive, info, data):
1016        new_data = []
1017        for l in results[1]:
1018            if l.startswith('%error'):
1019                l = self._expand(l)
1020                raise error.general('config error: %s' % (l[7:]))
1021            elif l.startswith('%warning'):
1022                l = self._expand(l)
1023                log.stderr('warning: %s' % (l[9:]))
1024                log.warning(l[9:])
1025            if not directive:
1026                l = self._expand(l)
1027                ls = self.tags.split(l, 1)
1028                log.trace('config: %s: _tag: %s %s' % (self.name, l, ls))
1029                if len(ls) > 1:
1030                    info = ls[0].lower()
1031                    if info[-1] == ':':
1032                        info = info[:-1]
1033                    info_data = ls[1].strip()
1034                else:
1035                    info_data = ls[0].strip()
1036                if info is not None:
1037                    self._info_append(info, info_data)
1038                else:
1039                    log.warning("invalid format: '%s'" % (info_data[:-1]))
1040            else:
1041                l = self._expand(l)
1042                log.trace('config: %s: _data: %s %s' % (self.name, l, new_data))
1043                new_data.append(l)
1044        return (directive, info, data + new_data)
1045
1046    def _set_package(self, _package):
1047        if self.package == 'main' and \
1048                self._packages[self.package].name() != None:
1049            if self._packages[self.package].name() == _package:
1050                return
1051        if _package not in self._packages:
1052            self._packages[_package] = package(_package,
1053                                               self.define('%{_arch}'),
1054                                               self)
1055        self.package = _package
1056
1057    def _directive_extend(self, dir, data):
1058        self._packages[self.package].directive_extend(dir, data)
1059
1060    def _info_append(self, info, data):
1061        self._packages[self.package].info_append(info, data)
1062
1063    def set_macros(self, macros):
1064        if macros is None:
1065            self.macros = opts.defaults
1066        else:
1067            self.macros = macros
1068
1069    def load(self, name):
1070
1071        def common_end(left, right):
1072            end = ''
1073            while len(left) and len(right):
1074                if left[-1] != right[-1]:
1075                    return end
1076                end = left[-1] + end
1077                left = left[:-1]
1078                right = right[:-1]
1079            return end
1080
1081        if self.load_depth == 0:
1082            self._reset(name)
1083            self._packages[self.package] = package(self.package,
1084                                                   self.define('%{_arch}'),
1085                                                   self)
1086
1087        self.load_depth += 1
1088
1089        save_name = self.name
1090        save_lc = self.lc
1091
1092        #
1093        # Locate the config file. Expand any macros then add the
1094        # extension. Check if the file exists, therefore directly
1095        # referenced. If not see if the file contains ':' or the path
1096        # separator. If it does split the path else use the standard config dir
1097        # path in the defaults.
1098        #
1099
1100        exname = self.expand(name)
1101
1102        #
1103        # Macro could add an extension.
1104        #
1105        if exname.endswith('.cfg'):
1106            configname = exname
1107        else:
1108            configname = '%s.cfg' % (exname)
1109            name = '%s.cfg' % (name)
1110
1111        if ':' in configname:
1112            cfgname = path.basename(configname)
1113        else:
1114            cfgname = common_end(configname, name)
1115
1116        if not path.exists(configname):
1117            if ':' in configname:
1118                configdirs = path.dirname(configname).split(':')
1119            else:
1120                configdirs = self.define('_configdir').split(':')
1121            for cp in configdirs:
1122                configname = path.join(path.abspath(cp), cfgname)
1123                if path.exists(configname):
1124                    break
1125                configname = None
1126            if configname is None:
1127                raise error.general('no config file found: %s' % (cfgname))
1128
1129        try:
1130            log.trace('config: %s: _open: %s' % (self.name, path.host(configname)))
1131            config = open(path.host(configname), 'r')
1132        except IOError, err:
1133            raise error.general('error opening config file: %s' % (path.host(configname)))
1134
1135        self.configpath += [configname]
1136        self._includes += [configname]
1137
1138        self.name = self._relative_path(configname)
1139        self.lc = 0
1140
1141        try:
1142            dir = None
1143            info = None
1144            data = []
1145            while True:
1146                r = self._parse(config, dir, info)
1147                if r[0] == 'package':
1148                    dir, info, data = self._process_package(r, dir, info, data)
1149                elif r[0] == 'control':
1150                    if r[1] == '%end':
1151                        break
1152                    log.warning("unexpected '%s'" % (r[1]))
1153                elif r[0] == 'directive':
1154                    if r[1] == '%include':
1155                        self.load(r[2][0])
1156                        continue
1157                    dir, info, data = self._process_directive(r, dir, info, data)
1158                elif r[0] == 'data':
1159                    dir, info, data = self._process_data(r, dir, info, data)
1160                else:
1161                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
1162            if dir is not None:
1163                self._directive_extend(dir, data)
1164        except:
1165            config.close()
1166            raise
1167
1168        config.close()
1169
1170        self.name = save_name
1171        self.lc = save_lc
1172
1173        self.load_depth -= 1
1174
1175    def defined(self, name):
1176        return self.macros.has_key(name)
1177
1178    def define(self, name):
1179        if name in self.macros:
1180            d = self.macros[name]
1181        else:
1182            n = self._label(name)
1183            if n in self.macros:
1184                d = self.macros[n]
1185            else:
1186                raise error.general('%d: macro "%s" not found' % (self.lc, name))
1187        return self._expand(d)
1188
1189    def set_define(self, name, value):
1190        self.macros[name] = value
1191
1192    def expand(self, line):
1193        if type(line) == list:
1194            el = []
1195            for l in line:
1196                el += [self._expand(l)]
1197            return el
1198        return self._expand(line)
1199
1200    def macro(self, name):
1201        if name in self.macros:
1202            return self.macros[name]
1203        raise error.general('macro "%s" not found' % (name))
1204
1205    def directive(self, _package, name):
1206        if _package not in self._packages:
1207            raise error.general('package "' + _package + '" not found')
1208        if name not in self._packages[_package].directives:
1209            raise error.general('directive "' + name + \
1210                                    '" not found in package "' + _package + '"')
1211        return self._packages[_package].directives[name]
1212
1213    def abspath(self, rpath):
1214        return path.abspath(self.define(rpath))
1215
1216    def packages(self):
1217        return self._packages
1218
1219    def includes(self):
1220        return self._includes
1221
1222    def file_name(self):
1223        return self.name
1224
1225def run():
1226    import sys
1227    try:
1228        #
1229        # Run where defaults.mc is located
1230        #
1231        opts = options.load(sys.argv, defaults = 'defaults.mc')
1232        log.trace('config: count %d' % (len(opts.config_files())))
1233        for config_file in opts.config_files():
1234            s = file(config_file, opts)
1235            print s
1236            del s
1237    except error.general, gerr:
1238        print gerr
1239        sys.exit(1)
1240    except error.internal, ierr:
1241        print ierr
1242        sys.exit(1)
1243    except KeyboardInterrupt:
1244        log.notice('abort: user terminated')
1245        sys.exit(1)
1246    sys.exit(0)
1247
1248if __name__ == "__main__":
1249    run()
Note: See TracBrowser for help on using the repository browser.