source: rtems-tools/rtemstoolkit/config.py @ 3e14594

4.105
Last change on this file since 3e14594 was 2de37f3, checked in by Chris Johns <chrisj@…>, on 03/09/16 at 03:27:42

Python 2 and python 3 refactor fixes.

Updates #2619.

  • Property mode set to 100644
File size: 31.9 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# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# This code is based on a tool I wrote to parse RPM spec files in the RTEMS
33# project. This is now a configuration file format that has moved away from the
34# spec file format to support the specific needs of cross-compiling GCC. This
35# module parses a configuration file into Python data types that can be used by
36# other software modules.
37#
38
39from __future__ import print_function
40
41import copy
42import functools
43import os
44import re
45import sys
46
47#
48# Support to handle use in a package and as a unit test.
49# If there is a better way to let us know.
50#
51try:
52    from . import error
53    from . import execute
54    from . import log
55    from . import options
56    from . import path
57except (ValueError, SystemError):
58    import error
59    import execute
60    import log
61    import options
62    import path
63
64def _check_bool(value):
65    if value.isdigit():
66        if int(value) == 0:
67            istrue = False
68        else:
69            istrue = True
70    else:
71        istrue = None
72    return istrue
73
74class file(object):
75    """Parse a config file."""
76
77    def __init__(self, name, opts, macros = None, directives = None, ignores = None):
78        self.opts = opts
79        if macros is None:
80            self.macros = opts.defaults
81        else:
82            self.macros = macros
83        self.init_name = name
84        self.directives = ['%include']
85        if directives:
86            self.directives += directives
87        self.ignores = ignores
88        log.trace('config: %s' % (name))
89        self.disable_macro_reassign = False
90        self.configpath = []
91        self.wss = re.compile(r'\s+')
92        self.tags = re.compile(r':+')
93        self.sf = re.compile(r'%\([^\)]+\)')
94        for arg in self.opts.args:
95            if arg.startswith('--with-') or arg.startswith('--without-'):
96                label = arg[2:].lower().replace('-', '_')
97                self.macros.define(label)
98        self._includes = []
99        self.load_depth = 0
100        self.lc = 0
101        self.name = 'none'
102
103    def __del__(self):
104        pass
105
106    def __str__(self):
107
108        def _dict(dd):
109            s = ''
110            ddl = list(dd.keys())
111            ddl.sort()
112            for d in ddl:
113                s += '  ' + d + ': ' + dd[d] + '\n'
114            return s
115
116        s = 'config: %s' % ('.'.join(self.configpath)) + \
117            '\n' + str(self.opts) + \
118            '\nlines parsed: %d' % (self.lc) + \
119            '\nname: ' + self.name + \
120            '\nmacros:\n' + str(self.macros)
121        return s
122
123    def _name_line_msg(self,  msg):
124        return '%s:%d: %s' % (path.basename(self.init_name), self.lc,  msg)
125
126    def _output(self, text):
127        if not self.opts.quiet():
128            log.output(text)
129
130    def _error(self, msg):
131        err = 'error: %s' % (self._name_line_msg(msg))
132        log.stderr(err)
133        log.output(err)
134        self.in_error = True
135        if not self.opts.dry_run():
136            log.stderr('warning: switched to dry run due to errors')
137            self.opts.set_dry_run()
138
139    def _label(self, name):
140        if name.startswith('%{') and name[-1] is '}':
141            return name
142        return '%{' + name.lower() + '}'
143
144    def _macro_split(self, s):
145        '''Split the string (s) up by macros. Only split on the
146           outter level. Nested levels will need to split with futher calls.'''
147        trace_me = False
148        if trace_me:
149            print('------------------------------------------------------')
150        macros = []
151        nesting = []
152        has_braces = False
153        c = 0
154        while c < len(s):
155            if trace_me:
156                print('ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting)
157            #
158            # We need to watch for shell type variables or the form '${var}' because
159            # they can upset the brace matching.
160            #
161            if s[c] == '%' or s[c] == '$':
162                start = s[c]
163                c += 1
164                if c == len(s):
165                    continue
166                #
167                # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ?
168                #
169                if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'):
170                    continue
171                elif not s[c].isspace():
172                    #
173                    # If this is a shell macro and we are at the outter
174                    # level or is '$var' forget it and move on.
175                    #
176                    if start == '$' and (s[c] != '{' or len(nesting) == 0):
177                        continue
178                    if s[c] == '{':
179                        this_has_braces = True
180                    else:
181                        this_has_braces = False
182                    nesting.append((c - 1, has_braces))
183                    has_braces = this_has_braces
184            elif len(nesting) > 0:
185                if s[c] == '}' or (s[c].isspace() and not has_braces):
186                    #
187                    # Can have '%{?test: something %more}' where the
188                    # nested %more ends with the '}' which also ends
189                    # the outter macro.
190                    #
191                    if not has_braces:
192                        if s[c] == '}':
193                            macro_start, has_braces = nesting[len(nesting) - 1]
194                            nesting = nesting[:-1]
195                            if len(nesting) == 0:
196                                macros.append(s[macro_start:c].strip())
197                    if len(nesting) > 0:
198                        macro_start, has_braces = nesting[len(nesting) - 1]
199                        nesting = nesting[:-1]
200                        if len(nesting) == 0:
201                            macros.append(s[macro_start:c + 1].strip())
202            c += 1
203        if trace_me:
204            print('ms:', macros)
205        if trace_me:
206            print('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
207        return macros
208
209    def _shell(self, line):
210        sl = self.sf.findall(line)
211        if len(sl):
212            e = execute.capture_execution()
213            for s in sl:
214                if options.host_windows:
215                    cmd = '%s -c "%s"' % (self.macros.expand('%{__sh}'), s[2:-1])
216                else:
217                    cmd = s[2:-1]
218                exit_code, proc, output = e.shell(cmd)
219                if exit_code == 0:
220                    line = line.replace(s, output)
221                else:
222                    raise error.general('shell macro failed: %s:%d: %s' % (s, exit_code, output))
223        return line
224
225    def _expand(self, s):
226        expand_count = 0
227        expanded = True
228        while expanded:
229            expand_count += 1
230            if expand_count > 500:
231                raise error.general('macro expand looping: %s' % (s))
232            expanded = False
233            ms = self._macro_split(s)
234            for m in ms:
235                mn = m
236                #
237                # A macro can be '%{macro}' or '%macro'. Turn the later into
238                # the former.
239                #
240                show_warning = True
241                if mn[1] != '{':
242                    if self.ignores is not None:
243                        for r in self.ignores:
244                            if r.match(mn) is not None:
245                                mn = None
246                                break
247                        else:
248                            mn = self._label(mn[1:])
249                            show_warning = False
250                    else:
251                        mn = self._label(mn[1:])
252                        show_warning = False
253                elif m.startswith('%{expand'):
254                    colon = m.find(':')
255                    if colon < 8:
256                        log.warning('malformed expand macro, no colon found')
257                    else:
258                        e = self._expand(m[colon + 1:-1].strip())
259                        s = s.replace(m, e)
260                        expanded = True
261                        mn = None
262                elif m.startswith('%{with '):
263                    #
264                    # Change the ' ' to '_' because the macros have no spaces.
265                    #
266                    n = self._label('with_' + m[7:-1].strip())
267                    if n in self.macros:
268                        s = s.replace(m, '1')
269                    else:
270                        s = s.replace(m, '0')
271                    expanded = True
272                    mn = None
273                elif m.startswith('%{echo'):
274                    if not m.endswith('}'):
275                        log.warning("malformed conditional macro '%s'" % (m))
276                        mn = None
277                    else:
278                        e = self._expand(m[6:-1].strip())
279                        log.output('%s' % (self._name_line_msg(e)))
280                        s = ''
281                        expanded = True
282                        mn = None
283                elif m.startswith('%{defined'):
284                    n = self._label(m[9:-1].strip())
285                    if n in self.macros:
286                        s = s.replace(m, '1')
287                    else:
288                        s = s.replace(m, '0')
289                    expanded = True
290                    mn = None
291                elif m.startswith('%{?') or m.startswith('%{!?'):
292                    if m[2] == '!':
293                        start = 4
294                    else:
295                        start = 3
296                    colon = m[start:].find(':')
297                    if colon < 0:
298                        if not m.endswith('}'):
299                            log.warning("malformed conditional macro '%s'" % (m))
300                            mn = None
301                        else:
302                            mn = self._label(m[start:-1])
303                    else:
304                        mn = self._label(m[start:start + colon])
305                    if mn:
306                        if m.startswith('%{?'):
307                            istrue = False
308                            if mn in self.macros:
309                                # If defined and 0 then it is false.
310                                istrue = _check_bool(self.macros[mn])
311                                if istrue is None:
312                                    istrue = True
313                            if colon >= 0 and istrue:
314                                s = s.replace(m, m[start + colon + 1:-1])
315                                expanded = True
316                                mn = None
317                            elif not istrue:
318                                mn = '%{nil}'
319                        else:
320                            isfalse = True
321                            if mn in self.macros:
322                                istrue = _check_bool(self.macros[mn])
323                                if istrue is None or istrue == True:
324                                    isfalse = False
325                            if colon >= 0 and isfalse:
326                                s = s.replace(m, m[start + colon + 1:-1])
327                                expanded = True
328                                mn = None
329                            else:
330                                mn = '%{nil}'
331                if mn:
332                    if mn.lower() in self.macros:
333                        s = s.replace(m, self.macros[mn.lower()])
334                        expanded = True
335                    elif show_warning:
336                        self._error("macro '%s' not found" % (mn))
337        return self._shell(s)
338
339    def _disable(self, config, ls):
340        if len(ls) != 2:
341            log.warning('invalid disable statement')
342        else:
343            if ls[1] == 'select':
344                self.macros.lock_read_map()
345                log.trace('config: %s: _disable_select: %s' % (self.init_name, ls[1]))
346            else:
347                log.warning('invalid disable statement: %s' % (ls[1]))
348
349    def _select(self, config, ls):
350        if len(ls) != 2:
351            log.warning('invalid select statement')
352        else:
353            r = self.macros.set_read_map(ls[1])
354            log.trace('config: %s: _select: %s %s %r' % \
355                          (self.init_name, r, ls[1], self.macros.maps()))
356
357    def _define(self, config, ls):
358        if len(ls) <= 1:
359            log.warning('invalid macro definition')
360        else:
361            d = self._label(ls[1])
362            if self.disable_macro_reassign:
363                if (d not in self.macros) or \
364                        (d in self.macros and len(self.macros[d]) == 0):
365                    if len(ls) == 2:
366                        self.macros[d] = '1'
367                    else:
368                        self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
369                else:
370                    log.warning("macro '%s' already defined" % (d))
371            else:
372                if len(ls) == 2:
373                    self.macros[d] = '1'
374                else:
375                    self.macros[d] = ' '.join([f.strip() for f in ls[2:]])
376
377    def _undefine(self, config, ls):
378        if len(ls) <= 1:
379            log.warning('invalid macro definition')
380        else:
381            mn = self._label(ls[1])
382            if mn in self.macros:
383                del self.macros[mn]
384            else:
385                log.warning("macro '%s' not defined" % (mn))
386
387    def _ifs(self, config, ls, label, iftrue, isvalid, dir, info):
388        in_iftrue = True
389        data = []
390        while True:
391            if isvalid and \
392                    ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)):
393                this_isvalid = True
394            else:
395                this_isvalid = False
396            r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid)
397            if r[0] == 'control':
398                if r[1] == '%end':
399                    self._error(label + ' without %endif')
400                    raise error.general('terminating build')
401                if r[1] == '%endif':
402                    log.trace('config: %s: _ifs: %s %s' % (self.init_name, r[1], this_isvalid))
403                    return data
404                if r[1] == '%else':
405                    in_iftrue = False
406            elif r[0] == 'directive':
407                if this_isvalid:
408                    if r[1] == '%include':
409                        self.load(r[2][0])
410                        continue
411                    dir, info, data = self._process_directive(r, dir, info, data)
412            elif r[0] == 'data':
413                if this_isvalid:
414                    dir, info, data = self._process_data(r, dir, info, data)
415            else:
416                dir, info, data = self._process_block(r, dir, info, data)
417
418        # @note is a directive extend missing
419
420    def _if(self, config, ls, isvalid, dir, info, invert = False):
421
422        def add(x, y):
423            return x + ' ' + str(y)
424
425        istrue = False
426        if isvalid:
427            if len(ls) == 2:
428                s = ls[1]
429            else:
430                s = (ls[1] + ' ' + ls[2])
431            ifls = s.split()
432            if len(ifls) == 1:
433                #
434                # Check if '%if %{x} == %{nil}' has both parts as nothing
435                # which means '%if ==' is always True and '%if !=' is always false.
436                #
437                if ifls[0] == '==':
438                    istrue = True
439                elif ifls[0] == '!=':
440                    istrue = False
441                else:
442                    istrue = _check_bool(ifls[0])
443                    if istrue == None:
444                        self._error('invalid if bool value: ' + functools.reduce(add, ls, ''))
445                        istrue = False
446            elif len(ifls) == 2:
447                if ifls[0] == '!':
448                    istrue = _check_bool(ifls[1])
449                    if istrue == None:
450                        self._error('invalid if bool value: ' + functools.reduce(add, ls, ''))
451                        istrue = False
452                    else:
453                        istrue = not istrue
454                else:
455                    #
456                    # Check is something is being checked against empty,
457                    #   ie '%if %{x} == %{nil}'
458                    # The logic is 'something == nothing' is False and
459                    # 'something != nothing' is True.
460                    #
461                    if ifls[1] == '==':
462                        istrue = False
463                    elif  ifls[1] == '!=':
464                        istrue = True
465                    else:
466                        self._error('invalid if bool operator: ' + functools.reduce(add, ls, ''))
467            elif len(ifls) == 3:
468                if ifls[1] == '==':
469                    if ifls[0] == ifls[2]:
470                        istrue = True
471                    else:
472                        istrue = False
473                elif ifls[1] == '!=' or ifls[1] == '=!':
474                    if ifls[0] != ifls[2]:
475                        istrue = True
476                    else:
477                        istrue = False
478                elif ifls[1] == '>':
479                    if ifls[0] > ifls[2]:
480                        istrue = True
481                    else:
482                        istrue = False
483                elif ifls[1] == '>=' or ifls[1] == '=>':
484                    if ifls[0] >= ifls[2]:
485                        istrue = True
486                    else:
487                        istrue = False
488                elif ifls[1] == '<=' or ifls[1] == '=<':
489                    if ifls[0] <= ifls[2]:
490                        istrue = True
491                    else:
492                        istrue = False
493                elif ifls[1] == '<':
494                    if ifls[0] < ifls[2]:
495                        istrue = True
496                    else:
497                        istrue = False
498                else:
499                    self._error('invalid %if operator: ' + functools.reduce(add, ls, ''))
500            else:
501                self._error('malformed if: ' + functools.reduce(add, ls, ''))
502            if invert:
503                istrue = not istrue
504            log.trace('config: %s: _if:  %s %s' % (self.init_name, ifls, str(istrue)))
505        return self._ifs(config, ls, '%if', istrue, isvalid, dir, info)
506
507    def _ifos(self, config, ls, isvalid, dir, info):
508        isos = False
509        if isvalid:
510            os = self.define('_os')
511            for l in ls:
512                if l in os:
513                    isos = True
514                    break
515        return self._ifs(config, ls, '%ifos', isos, isvalid, dir, info)
516
517    def _ifarch(self, config, positive, ls, isvalid, dir, info):
518        isarch = False
519        if isvalid:
520            arch = self.define('_arch')
521            for l in ls:
522                if l in arch:
523                    isarch = True
524                    break
525        if not positive:
526            isarch = not isarch
527        return self._ifs(config, ls, '%ifarch', isarch, isvalid, dir, info)
528
529    def _parse(self, config, dir, info, roc = False, isvalid = True):
530        # roc = return on control
531
532        def _clean(line):
533            line = line[0:-1]
534            b = line.find('#')
535            if b >= 0:
536                line = line[1:b]
537            return line.strip()
538
539        #
540        # Need to add code to count matching '{' and '}' and if they
541        # do not match get the next line and add to the string until
542        # they match. This closes an opening '{' that is on another
543        # line.
544        #
545        for l in config:
546            self.lc += 1
547            l = _clean(l)
548            if len(l) == 0:
549                continue
550            log.trace('config: %s: %03d: %s %s' % \
551                          (self.init_name, self.lc, str(isvalid), l))
552            lo = l
553            if isvalid:
554                l = self._expand(l)
555            if len(l) == 0:
556                continue
557            if l[0] == '%':
558                ls = self.wss.split(l, 2)
559                los = self.wss.split(lo, 2)
560                if ls[0] == '%disable':
561                    if isvalid:
562                        self._disable(config, ls)
563                elif ls[0] == '%select':
564                    if isvalid:
565                        self._select(config, ls)
566                elif ls[0] == '%error':
567                    if isvalid:
568                        return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))])
569                elif ls[0] == '%warning':
570                    if isvalid:
571                        return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))])
572                elif ls[0] == '%define' or ls[0] == '%global':
573                    if isvalid:
574                        self._define(config, ls)
575                elif ls[0] == '%undefine':
576                    if isvalid:
577                        self._undefine(config, ls)
578                elif ls[0] == '%if':
579                    d = self._if(config, ls, isvalid, dir, info)
580                    if len(d):
581                        log.trace('config: %s: %%if: %s' % (self.init_name, d))
582                        return ('data', d)
583                elif ls[0] == '%ifn':
584                    d = self._if(config, ls, isvalid, dir, info, True)
585                    if len(d):
586                        log.trace('config: %s: %%ifn: %s' % (self.init_name, d))
587                        return ('data', d)
588                elif ls[0] == '%ifos':
589                    d = self._ifos(config, ls, isvalid, dir, info)
590                    if len(d):
591                        return ('data', d)
592                elif ls[0] == '%ifarch':
593                    d = self._ifarch(config, True, ls, isvalid, dir, info)
594                    if len(d):
595                        return ('data', d)
596                elif ls[0] == '%ifnarch':
597                    d = self._ifarch(config, False, ls, isvalid, dir, info)
598                    if len(d):
599                        return ('data', d)
600                elif ls[0] == '%endif':
601                    if roc:
602                        return ('control', '%endif', '%endif')
603                    log.warning("unexpected '" + ls[0] + "'")
604                elif ls[0] == '%else':
605                    if roc:
606                        return ('control', '%else', '%else')
607                    log.warning("unexpected '" + ls[0] + "'")
608                elif ls[0].startswith('%defattr'):
609                    return ('data', [l])
610                elif ls[0] == '%bcond_with':
611                    if isvalid:
612                        #
613                        # Check if already defined. Would be by the command line or
614                        # even a host specific default.
615                        #
616                        if self._label('with_' + ls[1]) not in self.macros:
617                            self._define(config, (ls[0], 'without_' + ls[1]))
618                elif ls[0] == '%bcond_without':
619                    if isvalid:
620                        if self._label('without_' + ls[1]) not in self.macros:
621                            self._define(config, (ls[0], 'with_' + ls[1]))
622                else:
623                    pt = self._parse_token(lo, los, l, ls)
624                    if pt is not None:
625                        return pt
626                    if self.ignores is not None:
627                        for r in self.ignores:
628                            if r.match(ls[0]) is not None:
629                                return ('data', [l])
630                    if isvalid:
631                        for d in self.directives:
632                            if ls[0].strip() == d:
633                                return ('directive', ls[0].strip(), ls[1:])
634                        log.warning("unknown directive: '" + ls[0] + "'")
635                        return ('data', [lo])
636            else:
637                return ('data', [lo])
638        return ('control', '%end', '%end')
639
640    def _parse_token(self, line, line_split, line_expanded, line_split_expanded):
641        return None
642
643    def _process_directive(self, results, directive, info, data):
644        new_data = []
645        if results[1] == '%description':
646            new_data = [' '.join(results[2])]
647        else:
648            directive, into, data = self._directive_filter(results, directive, info, data)
649        if directive and directive != results[1]:
650            self._directive_extend(directive, data)
651        directive = results[1]
652        data = new_data
653        return (directive, info, data)
654
655    def _process_data(self, results, directive, info, data):
656        new_data = []
657        for l in results[1]:
658            if l.startswith('%error'):
659                l = self._expand(l)
660                raise error.general('config error: %s' % (l[7:]))
661            elif l.startswith('%warning'):
662                l = self._expand(l)
663                log.stderr('warning: %s' % (l[9:]))
664                log.warning(l[9:])
665            if not directive:
666                l = self._expand(l)
667                ls = self.tags.split(l, 1)
668                log.trace('config: %s: _tag: %s %s' % (self.init_name, l, ls))
669                if len(ls) > 1:
670                    info = ls[0].lower()
671                    if info[-1] == ':':
672                        info = info[:-1]
673                    info_data = ls[1].strip()
674                else:
675                    info_data = ls[0].strip()
676                if info is not None:
677                    self._info_append(info, info_data)
678                else:
679                    log.warning("invalid format: '%s'" % (info_data[:-1]))
680            else:
681                log.trace('config: %s: _data: %s %s' % (self.init_name, l, new_data))
682                new_data.append(l)
683        return (directive, info, data + new_data)
684
685    def _process_block(self, results, directive, info, data):
686        raise error.internal('known block type: %s' % (results[0]))
687
688    def _directive_extend(self, dir, data):
689        pass
690
691    def _directive_filter(self, results, directive, info, data):
692        return directive, into, data
693
694    def _info_append(self, info, data):
695        pass
696
697    def load(self, name):
698
699        def common_end(left, right):
700            end = ''
701            while len(left) and len(right):
702                if left[-1] != right[-1]:
703                    return end
704                end = left[-1] + end
705                left = left[:-1]
706                right = right[:-1]
707            return end
708
709        if self.load_depth == 0:
710            self.in_error = False
711            self.lc = 0
712            self.name = name
713            self.conditionals = {}
714
715        self.load_depth += 1
716
717        save_name = self.name
718        save_lc = self.lc
719
720        self.name = name
721        self.lc = 0
722
723        #
724        # Locate the config file. Expand any macros then add the
725        # extension. Check if the file exists, therefore directly
726        # referenced. If not see if the file contains ':' or the path
727        # separator. If it does split the path else use the standard config dir
728        # path in the defaults.
729        #
730        exname = self.expand(name)
731
732        #
733        # Macro could add an extension.
734        #
735        if exname.endswith('.cfg'):
736            configname = exname
737        else:
738            configname = '%s.cfg' % (exname)
739            name = '%s.cfg' % (name)
740
741        if ':' in configname:
742            cfgname = path.basename(configname)
743        else:
744            cfgname = common_end(configname, name)
745
746        if not path.exists(configname):
747            if ':' in configname:
748                configdirs = path.dirname(configname).split(':')
749            else:
750                configdirs = self.define('_configdir').split(':')
751            for cp in configdirs:
752                configname = path.join(path.abspath(cp), cfgname)
753                if path.exists(configname):
754                    break
755                configname = None
756            if configname is None:
757                raise error.general('no config file found: %s' % (cfgname))
758
759        try:
760            log.trace('config: %s: _open: %s' % (self.init_name, path.host(configname)))
761            config = open(path.host(configname), 'r')
762        except IOError as err:
763            raise error.general('error opening config file: %s' % (path.host(configname)))
764        self.configpath += [configname]
765
766        self._includes += [configname]
767
768        try:
769            dir = None
770            info = None
771            data = []
772            while True:
773                r = self._parse(config, dir, info)
774                if r[0] == 'control':
775                    if r[1] == '%end':
776                        break
777                    log.warning("unexpected '%s'" % (r[1]))
778                elif r[0] == 'directive':
779                    if r[1] == '%include':
780                        self.load(r[2][0])
781                        continue
782                    dir, info, data = self._process_directive(r, dir, info, data)
783                elif r[0] == 'data':
784                    dir, info, data = self._process_data(r, dir, info, data)
785                else:
786                    self._error("%d: invalid parse state: '%s" % (self.lc, r[0]))
787            if dir is not None:
788                self._directive_extend(dir, data)
789        except:
790            config.close()
791            raise
792
793        config.close()
794
795        self.name = save_name
796        self.lc = save_lc
797
798        self.load_depth -= 1
799
800    def defined(self, name):
801        return self.macros.has_key(name)
802
803    def define(self, name):
804        if name in self.macros:
805            d = self.macros[name]
806        else:
807            n = self._label(name)
808            if n in self.macros:
809                d = self.macros[n]
810            else:
811                raise error.general('%d: macro "%s" not found' % (self.lc, name))
812        return self._expand(d)
813
814    def set_define(self, name, value):
815        self.macros[name] = value
816
817    def expand(self, line):
818        if type(line) == list:
819            el = []
820            for l in line:
821                el += [self._expand(l)]
822            return el
823        return self._expand(line)
824
825    def macro(self, name):
826        if name in self.macros:
827            return self.macros[name]
828        raise error.general('macro "%s" not found' % (name))
829
830    def directive(self, name):
831        pass
832
833    def abspath(self, rpath):
834        return path.abspath(self.define(rpath))
835
836    def includes(self):
837        return self._includes
838
839    def file_name(self):
840        return self.init_name
841
842def run():
843    import sys
844    try:
845        #
846        # Run where defaults.mc is located
847        #
848        long_opts = {
849            # key              macro        handler   param  defs   init
850            '--file'  :      ('_file',      'path',   True,  None,  False)
851        }
852        opts = options.command_line(base_path = '.',
853                                    argv = sys.argv,
854                                    long_opts = long_opts)
855        options.load(opts)
856        s = file(opts.defaults['_file'], opts)
857        s.load(opts.defaults['_file'])
858        print(s)
859        del s
860    except error.general as gerr:
861        print(gerr)
862        sys.exit(1)
863    except error.internal as ierr:
864        print(ierr)
865        sys.exit(1)
866    except KeyboardInterrupt:
867        log.notice('abort: user terminated')
868        sys.exit(1)
869    sys.exit(0)
870
871if __name__ == "__main__":
872    run()
Note: See TracBrowser for help on using the repository browser.