source: rtems-source-builder/source-builder/sb/macros.py @ cb12e48

4.104.114.9
Last change on this file since cb12e48 was cb12e48, checked in by Chris Johns <chrisj@…>, on Apr 9, 2013 at 3:51:43 AM

Refactor defaults, macros and options.

To support building snapshots and pre-release source the defaults
has been refactored. The defaults have been moved to a stand alone
file and a macros.py module added. This modile abstracts the
old default dictionary turning it into a class. The macros
class can load macros from a file therefore the defaults have
been moved to a stand alone file.

The use of defaults has been removed from the project. The only
case where it is used in the options where the defaults are read
from a file. Macros are used everywhere now.

The defaults.py has been moved to the option.py and the separate
options and defaults values has been moved to a new pattern. When
constructing an object that needs macros and options if the macros
passed in is None the defaults from the options are used. This makes
it clear when the defaults are being used or when a modified set of
macros is being used.

The macros class support maps. The default is 'global' and where all
the defaults reside and where configuratiion file changes end up.
Maps allow macros to be read from a file and override the values
being maintained in the 'global' map. Reading a macro first checks
the map and if not present checks the 'global' map.

The addition of maps to the macros provides the base to support
snapshots and pre-release testing with standard configurations.
This functionality needs to be added. It works by letting to
specify a snapshot with:

source0: none, override, 'my-dist.tar.bz2'

and it will be used rather the value from the standard configuration.
With a build set you need to also specify the package these macros
are for. The maps provide this.

  • Property mode set to 100644
File size: 11.8 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2013 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#
21# Macro tables.
22#
23
24import re
25import os
26import string
27
28import error
29import path
30
31#
32# Macro tables
33#
34class macros:
35
36    class macro_iterator:
37        def __init__(self, keys):
38            self.keys = keys
39            self.index = 0
40
41        def __iter__(self):
42            return self
43
44        def next(self):
45            if self.index < len(self.keys):
46                key = self.keys[self.index]
47                self.index += 1
48                return key
49            raise StopIteration
50
51        def iterkeys(self):
52            return self.keys
53
54    def __init__(self, name = None, original = None, sbdir = '.'):
55        self.macro_filter = re.compile(r'%{[^}]+}')
56        if original is None:
57            self.macros = {}
58            self.map = 'global'
59            self.macros[self.map] = {}
60            self.macros[self.map]['_cwd'] = ('dir', 'required', path.shell(os.getcwd()))
61            self.macros[self.map]['_sbdir'] = ('dir', 'required', path.shell(sbdir))
62        else:
63            self.macros = {}
64            for m in original.macros:
65                if m not in self.macros:
66                    self.macros[m] = {}
67                for k in original.macros[m]:
68                    self.macros[m][k] = original.macros[m][k]
69            self.map = original.map
70        if name is not None:
71            self.load(name)
72
73    def __copy__(self):
74        return macros(original = self)
75
76    def __str__(self):
77        text_len = 80
78        text = ''
79        for map in self.macros:
80            text += '[%s]%s' % (map, os.linesep)
81            for k in sorted(self.macros[map].keys()):
82                d = self.macros[map][k]
83                text += " %s:%s '%s'%s '%s'%s" % \
84                    (k, ' ' * (20 - len(k)),
85                     d[0], ' ' * (8 - len(d[0])),
86                     d[1], ' ' * (10 - len(d[1])))
87                if len(d[2]) == 0:
88                    text += "''%s" % (os.linesep)
89                else:
90                    if '\n' in d[2]:
91                        text += "'''"
92                    else:
93                        text += "'"
94                indent = False
95                ds = d[2].split('\n')
96                lc = 0
97                for l in ds:
98                    lc += 1
99                    while len(l):
100                        if indent:
101                            text += ' %21s %10s %12s' % (' ', ' ', ' ')
102                        text += l[0:text_len]
103                        l = l[text_len:]
104                        if len(l):
105                            text += ' \\'
106                        elif lc == len(ds):
107                            if len(ds) > 1:
108                                text += "'''"
109                            else:
110                                text += "'"
111                        text += '%s' % (os.linesep)
112                        indent = True
113        return text
114
115    def __iter__(self):
116        return macros.macro_iterator(self.macros[self.map].keys())
117
118    def __getitem__(self, key):
119        macro = self.get(key)
120        if macro is None:
121            raise IndexError('key: %s' % (key))
122        return macro[2]
123
124    def __setitem__(self, key, value):
125        if type(key) is not str:
126            raise TypeError('bad key type (want str): %s' % (type(key)))
127        if type(value) is str:
128            value = ('none', 'none', value)
129        if type(value) is not tuple:
130            raise TypeError('bad value type (want tuple): %s' % (type(value)))
131        if len(value) != 3:
132            raise TypeError('bad value tuple (len not 3): %d' % (len(value)))
133        if type(value[0]) is not str:
134            raise TypeError('bad value tuple type field: %s' % (type(value[0])))
135        if type(value[1]) is not str:
136            raise TypeError('bad value tuple attrib field: %s' % (type(value[1])))
137        if type(value[2]) is not str:
138            raise TypeError('bad value tuple value field: %s' % (type(value[2])))
139        if value[0] not in ['none', 'triplet', 'dir', 'file', 'exe']:
140            raise TypeError('bad value tuple (type field): %s' % (value[0]))
141        if value[1] not in ['none', 'optional', 'required', 'override']:
142            raise TypeError('bad value tuple (attrib field): %s' % (value[1]))
143        self.macros[self.map][self.key_filter(key)] = value
144
145    def __delitem__(self, key):
146        self.undefine(key)
147
148    def __contains__(self, key):
149        return self.has_key(key)
150
151    def __len__(self):
152        return len(self.keys())
153
154    def keys(self):
155        k = self.macros[self.map].keys()
156        if map is not 'global':
157            k += self.macros['global'].keys()
158        return sorted(set(k))
159
160    def has_key(self, key):
161        if type(key) is not str:
162            raise TypeError('bad key type (want str): %s' % (type(key)))
163        key = self.key_filter(key)
164        if key not in self.macros[self.map].keys():
165            if key not in self.macros['global'].keys():
166                return False
167        return True
168
169    def key_filter(self, key):
170        if key.startswith('%{') and key[-1] is '}':
171            key = key[2:-1]
172        return key.lower()
173
174    def parse(self, lines):
175        macros = { 'global': {} }
176        map = 'global'
177        lc = 0
178        state = 'key'
179        token = ''
180        macro = []
181        for l in lines:
182            lc += 1
183            #print 'l:%s' % (l[:-1])
184            if len(l) == 0:
185                continue
186            for c in l:
187                #print ']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % (c, ord(c), state, token, macro, map)
188                if c is '#' and not state.startswith('value'):
189                    break
190                if c == '\n' or c == '\r':
191                    if not (state is 'key' and len(token) == 0) and \
192                            not state.startswith('value-multiline'):
193                        raise error.general('malformed macro line:%d: %s' % (lc, l))
194                if state is 'key':
195                    if c not in string.whitespace:
196                        if c is '[':
197                            state = 'map'
198                        elif c is ':':
199                            macro += [token]
200                            token = ''
201                            state = 'attribs'
202                        elif c is '#':
203                            break
204                        else:
205                            token += c
206                elif state is 'map':
207                    if c is ']':
208                        if token not in macros:
209                            macros[token] = {}
210                        map = token
211                        token = ''
212                        state = 'key'
213                    elif c in string.ascii_letters or c in string.digits:
214                        token += c
215                    else:
216                        raise error.general('invalid macro map:%d: %s' % (lc, l))
217                elif state is 'attribs':
218                    if c not in string.whitespace:
219                        if c is ',':
220                            macro += [token]
221                            token = ''
222                            if len(macro) == 3:
223                                state = 'value-start'
224                        else:
225                            token += c
226                elif state is 'value-start':
227                    if c is "'":
228                        state = 'value-line-start'
229                elif state is 'value-line-start':
230                    if c is "'":
231                        state = 'value-multiline-start'
232                    else:
233                        state = 'value-line'
234                        token += c
235                elif state is 'value-multiline-start':
236                    if c is "'":
237                        state = 'value-multiline'
238                    else:
239                        macro += [token]
240                        state = 'macro'
241                elif state is 'value-line':
242                    if c is "'":
243                        macro += [token]
244                        state = 'macro'
245                    else:
246                        token += c
247                elif state is 'value-multiline':
248                    if c is "'":
249                        state = 'value-multiline-end'
250                    else:
251                        token += c
252                elif state is 'value-multiline-end':
253                    if c is "'":
254                        state = 'value-multiline-end-end'
255                    else:
256                        state = 'value-multiline'
257                        token += "'" + c
258                elif state is 'value-multiline-end-end':
259                    if c is "'":
260                        macro += [token]
261                        state = 'macro'
262                    else:
263                        state = 'value-multiline'
264                        token += "''" + c
265                else:
266                    raise error.internal('bad state: %s' % (state))
267                if state is 'macro':
268                    macros[map][macro[0]] = (macro[1], macro[2], macro[3])
269                    macro = []
270                    token = ''
271                    state = 'key'
272        return macros
273
274    def load(self, name):
275        try:
276            name = self.expand(name)
277            mc = open(name, 'r')
278        except IOError, err:
279            raise error.general('opening macro file: %s' % (path.host(name)))
280        macros = self.parse(mc)
281        for m in macros:
282            for mm in macros[m]:
283                self.macros[m][mm] = macros[m][mm]
284        mc.close()
285
286    def get(self, key):
287        if type(key) is not str:
288            raise TypeError('bad key type: %s' % (type(key)))
289        key = self.key_filter(key)
290        if self.map is not 'global'and key in self.macros[self.map]:
291            return self.macros[self.map][key]
292        if key in self.macros['global']:
293            return self.macros['global'][key]
294        return None
295
296    def define(self, key, value = '1'):
297        if type(key) is not str:
298            raise TypeError('bad key type: %s' % (type(key)))
299        self.__setitem__(key, ('none', 'none', value))
300
301    def undefine(self, key):
302        if type(key) is not str:
303            raise TypeError('bad key type: %s' % (type(key)))
304        key = self.key_filter(key)
305        for map in self.macros:
306            if key in self.macros[map]:
307                del self.macros[map][key]
308
309    def expand(self, _str):
310        """Simple basic expander of config file macros."""
311        expanded = True
312        while expanded:
313            expanded = False
314            for m in self.macro_filter.findall(_str):
315                name = m[2:-1]
316                macro = self.get(name)
317                if macro is None:
318                    raise error.general('cannot expand default macro: %s in "%s"' %
319                                        (m, _str))
320                _str = _str.replace(m, macro[2])
321                expanded = True
322        return _str
323
324if __name__ == "__main__":
325    import copy
326    import sys
327    m = macros(name = 'defaults.mc')
328    d = copy.copy(m)
329    m['test1'] = 'something'
330    if d.has_key('test1'):
331        print 'error: copy failed.'
332        sys.exit(1)
333    print m
Note: See TracBrowser for help on using the repository browser.