source: rtems-tools/rtemstoolkit/config.py @ 156b227

5
Last change on this file since 156b227 was 7d3350d, checked in by Chris Johns <chrisj@…>, on 04/24/17 at 14:31:44

rtemstoolkit: Move host support access into a separate module.

Moving the host support into a module lets it get used where options
is not being used.

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