source: rtems-tools/rtemstoolkit/config.py @ 7e5cdea

Last change on this file since 7e5cdea was 7e5cdea, checked in by Chris Johns <chrisj@…>, on Nov 23, 2018 at 4:02:52 AM

rtemstoolkit: Add unit testing for the python modules

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