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

4.104.95
Last change on this file since c98555c was c98555c, checked in by Chris Johns <chrisj@…>, on 04/15/16 at 01:43:21

sb: Do not filter -f options from a BSP.

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