source: rtems-source-builder/source-builder/sb/macros.py @ 9bd29bb

4.104.114.9
Last change on this file since 9bd29bb was 9bd29bb, checked in by Chris Johns <chrisj@…>, on Apr 13, 2013 at 12:30:39 AM

Macros updates to support multiple maps.

Add a read and write map pointer. This means you can read from a
user defined map through to the global map while pointing all
write to only the global map therefore supporting overrides
cleanly.

Print the list of loaded files when printing.

Provide helper calls for type and attributes.

  • Property mode set to 100644
File size: 13.0 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.files = []
56        self.macro_filter = re.compile(r'%{[^}]+}')
57        if original is None:
58            self.macros = {}
59            self.read_map = 'global'
60            self.write_map = 'global'
61            self.macros[self.read_map] = {}
62            self.macros[self.read_map]['_cwd'] = ('dir', 'required', path.shell(os.getcwd()))
63            self.macros[self.read_map]['_sbdir'] = ('dir', 'required', path.shell(sbdir))
64        else:
65            self.macros = {}
66            for m in original.macros:
67                if m not in self.macros:
68                    self.macros[m] = {}
69                for k in original.macros[m]:
70                    self.macros[m][k] = original.macros[m][k]
71            self.read_map = original.read_map
72            self.write_map = original.write_map
73        if name is not None:
74            self.load(name)
75
76    def __copy__(self):
77        return macros(original = self)
78
79    def __str__(self):
80        text_len = 80
81        text = ''
82        for f in self.files:
83            text += '> %s%s' % (f, os.linesep)
84        for map in self.macros:
85            text += '[%s]%s' % (map, os.linesep)
86            for k in sorted(self.macros[map].keys()):
87                d = self.macros[map][k]
88                text += " %s:%s '%s'%s '%s'%s" % \
89                    (k, ' ' * (20 - len(k)),
90                     d[0], ' ' * (8 - len(d[0])),
91                     d[1], ' ' * (10 - len(d[1])))
92                if len(d[2]) == 0:
93                    text += "''%s" % (os.linesep)
94                else:
95                    if '\n' in d[2]:
96                        text += "'''"
97                    else:
98                        text += "'"
99                indent = False
100                ds = d[2].split('\n')
101                lc = 0
102                for l in ds:
103                    lc += 1
104                    while len(l):
105                        if indent:
106                            text += ' %21s %10s %12s' % (' ', ' ', ' ')
107                        text += l[0:text_len]
108                        l = l[text_len:]
109                        if len(l):
110                            text += ' \\'
111                        elif lc == len(ds):
112                            if len(ds) > 1:
113                                text += "'''"
114                            else:
115                                text += "'"
116                        text += '%s' % (os.linesep)
117                        indent = True
118        return text
119
120    def __iter__(self):
121        return macros.macro_iterator(self.macros[self.read_map].keys())
122
123    def __getitem__(self, key):
124        macro = self.get(key)
125        if macro is None:
126            raise IndexError('key: %s' % (key))
127        return macro[2]
128
129    def __setitem__(self, key, value):
130        if type(key) is not str:
131            raise TypeError('bad key type (want str): %s' % (type(key)))
132        if type(value) is str:
133            value = ('none', 'none', value)
134        if type(value) is not tuple:
135            raise TypeError('bad value type (want tuple): %s' % (type(value)))
136        if len(value) != 3:
137            raise TypeError('bad value tuple (len not 3): %d' % (len(value)))
138        if type(value[0]) is not str:
139            raise TypeError('bad value tuple type field: %s' % (type(value[0])))
140        if type(value[1]) is not str:
141            raise TypeError('bad value tuple attrib field: %s' % (type(value[1])))
142        if type(value[2]) is not str:
143            raise TypeError('bad value tuple value field: %s' % (type(value[2])))
144        if value[0] not in ['none', 'triplet', 'dir', 'file', 'exe']:
145            raise TypeError('bad value tuple (type field): %s' % (value[0]))
146        if value[1] not in ['none', 'optional', 'required', 'override']:
147            raise TypeError('bad value tuple (attrib field): %s' % (value[1]))
148        self.macros[self.write_map][self.key_filter(key)] = value
149
150    def __delitem__(self, key):
151        self.undefine(key)
152
153    def __contains__(self, key):
154        return self.has_key(key)
155
156    def __len__(self):
157        return len(self.keys())
158
159    def keys(self):
160        k = self.macros[self.read_map].keys()
161        if map is not 'global':
162            k += self.macros['global'].keys()
163        return sorted(set(k))
164
165    def has_key(self, key):
166        if type(key) is not str:
167            raise TypeError('bad key type (want str): %s' % (type(key)))
168        key = self.key_filter(key)
169        if key not in self.macros[self.read_map].keys():
170            if key not in self.macros['global'].keys():
171                return False
172        return True
173
174    def maps(self):
175        return self.macros.keys()
176
177    def key_filter(self, key):
178        if key.startswith('%{') and key[-1] is '}':
179            key = key[2:-1]
180        return key.lower()
181
182    def parse(self, lines):
183        macros = { 'global': {} }
184        map = 'global'
185        lc = 0
186        state = 'key'
187        token = ''
188        macro = []
189        for l in lines:
190            lc += 1
191            #print 'l:%s' % (l[:-1])
192            if len(l) == 0:
193                continue
194            for c in l:
195                #print ']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % (c, ord(c), state, token, macro, map)
196                if c is '#' and not state.startswith('value'):
197                    break
198                if c == '\n' or c == '\r':
199                    if not (state is 'key' and len(token) == 0) and \
200                            not state.startswith('value-multiline'):
201                        raise error.general('malformed macro line:%d: %s' % (lc, l))
202                if state is 'key':
203                    if c not in string.whitespace:
204                        if c is '[':
205                            state = 'map'
206                        elif c is ':':
207                            macro += [token]
208                            token = ''
209                            state = 'attribs'
210                        elif c is '#':
211                            break
212                        else:
213                            token += c
214                elif state is 'map':
215                    if c is ']':
216                        if token not in macros:
217                            macros[token] = {}
218                        map = token
219                        token = ''
220                        state = 'key'
221                    elif c in string.printable and c not in string.whitespace:
222                        token += c
223                    else:
224                        raise error.general('invalid macro map:%d: %s' % (lc, l))
225                elif state is 'attribs':
226                    if c not in string.whitespace:
227                        if c is ',':
228                            macro += [token]
229                            token = ''
230                            if len(macro) == 3:
231                                state = 'value-start'
232                        else:
233                            token += c
234                elif state is 'value-start':
235                    if c is "'":
236                        state = 'value-line-start'
237                elif state is 'value-line-start':
238                    if c is "'":
239                        state = 'value-multiline-start'
240                    else:
241                        state = 'value-line'
242                        token += c
243                elif state is 'value-multiline-start':
244                    if c is "'":
245                        state = 'value-multiline'
246                    else:
247                        macro += [token]
248                        state = 'macro'
249                elif state is 'value-line':
250                    if c is "'":
251                        macro += [token]
252                        state = 'macro'
253                    else:
254                        token += c
255                elif state is 'value-multiline':
256                    if c is "'":
257                        state = 'value-multiline-end'
258                    else:
259                        token += c
260                elif state is 'value-multiline-end':
261                    if c is "'":
262                        state = 'value-multiline-end-end'
263                    else:
264                        state = 'value-multiline'
265                        token += "'" + c
266                elif state is 'value-multiline-end-end':
267                    if c is "'":
268                        macro += [token]
269                        state = 'macro'
270                    else:
271                        state = 'value-multiline'
272                        token += "''" + c
273                else:
274                    raise error.internal('bad state: %s' % (state))
275                if state is 'macro':
276                    macros[map][macro[0].lower()] = (macro[1], macro[2], macro[3])
277                    macro = []
278                    token = ''
279                    state = 'key'
280        return macros
281
282    def load(self, name):
283        try:
284            name = self.expand(name)
285            mc = open(name, 'r')
286        except IOError, err:
287            raise error.general('opening macro file: %s' % (path.host(name)))
288        macros = self.parse(mc)
289        mc.close()
290        for m in macros:
291            if m not in self.macros:
292                self.macros[m] = {}
293            for mm in macros[m]:
294                self.macros[m][mm] = macros[m][mm]
295        self.files += [name]
296
297    def get(self, key):
298        if type(key) is not str:
299            raise TypeError('bad key type: %s' % (type(key)))
300        key = self.key_filter(key)
301        if self.read_map is not 'global'and key in self.macros[self.read_map]:
302            return self.macros[self.read_map][key]
303        if key in self.macros['global']:
304            return self.macros['global'][key]
305        return None
306
307    def get_type(self, key):
308        m = self.get(key)
309        if m is None:
310            return None
311        return m[0]
312
313    def get_attribute(self, key):
314        m = self.get(key)
315        if m is None:
316            return None
317        return m[1]
318
319    def overridden(self, key):
320        return self.get_attribute(key) == 'override'
321
322    def define(self, key, value = '1'):
323        if type(key) is not str:
324            raise TypeError('bad key type: %s' % (type(key)))
325        self.__setitem__(key, ('none', 'none', value))
326
327    def undefine(self, key):
328        if type(key) is not str:
329            raise TypeError('bad key type: %s' % (type(key)))
330        key = self.key_filter(key)
331        for map in self.macros:
332            if key in self.macros[map]:
333                del self.macros[map][key]
334
335    def expand(self, _str):
336        """Simple basic expander of config file macros."""
337        expanded = True
338        while expanded:
339            expanded = False
340            for m in self.macro_filter.findall(_str):
341                name = m[2:-1]
342                macro = self.get(name)
343                if macro is None:
344                    raise error.general('cannot expand default macro: %s in "%s"' %
345                                        (m, _str))
346                _str = _str.replace(m, macro[2])
347                expanded = True
348        return _str
349
350    def find(self, regex):
351        what = re.compile(regex)
352        keys = []
353        for key in self.keys():
354            if what.match(key):
355                keys += [key]
356        return keys
357
358    def set_read_map(self, map):
359        if map in self.macros:
360            self.read_map = map
361            return True
362        return False
363
364    def set_write_map(self, map):
365        if map in self.macros:
366            self.write_map = map
367            return True
368        return False
369
370if __name__ == "__main__":
371    import copy
372    import sys
373    m = macros(name = 'defaults.mc')
374    d = copy.copy(m)
375    m['test1'] = 'something'
376    if d.has_key('test1'):
377        print 'error: copy failed.'
378        sys.exit(1)
379    print m
Note: See TracBrowser for help on using the repository browser.