source: rtems-tools/rtemstoolkit/config.py @ 04a5204

4.10
Last change on this file since 04a5204 was 04a5204, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 12, 2015 at 10:15:23 AM

Python 3 compatibility

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