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

4.104.114.95
Last change on this file since a083b52 was a083b52, checked in by Chris Johns <chrisj@…>, on 07/29/14 at 06:35:43

Add checksum support for downloaded files.

File download by http, ftp, pw support checksum. The %hash
directive provides a means of setting a hash used to
checksum the file.

Files on disk or just downloaded are checked.

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