source: rtems-source-builder/source-builder/sb/macros.py @ 0565e1f

4.104.114.9
Last change on this file since 0565e1f was 0565e1f, checked in by Chris Johns <chrisj@…>, on Apr 13, 2013 at 8:29:30 AM

Add support for snapshot testing.

User macro files passed on the command line allow a user to
override the defaults in configuration files to test new changes
in pending releases.

Fix macros issues with keys with more than one map.

  • Property mode set to 100644
File size: 13.6 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.keys())
122
123    def __getitem__(self, key):
124        macro = self.get(key)
125        if macro is None or macro[1] == 'undefine':
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', 'undefine']:
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
161        def _map_keys(_map):
162            u = []
163            k = []
164            for mk in _map:
165                if _map[mk][1] == 'undefine':
166                    u += [mk]
167                else:
168                    k += [mk]
169            return k, u
170
171        keys, undefined = _map_keys(self.macros[self.read_map])
172        if map is not 'global':
173            gk, u = _map_keys(self.macros['global'])
174            undefined = set(undefined + u)
175            for k in gk:
176                if k not in undefined:
177                    keys += [k]
178        return sorted(set(keys))
179
180    def has_key(self, key):
181        if type(key) is not str:
182            raise TypeError('bad key type (want str): %s' % (type(key)))
183        if self.key_filter(key) not in self.keys():
184            return False
185        return True
186
187    def maps(self):
188        return self.macros.keys()
189
190    def key_filter(self, key):
191        if key.startswith('%{') and key[-1] is '}':
192            key = key[2:-1]
193        return key.lower()
194
195    def parse(self, lines):
196        macros = { 'global': {} }
197        map = 'global'
198        lc = 0
199        state = 'key'
200        token = ''
201        macro = []
202        for l in lines:
203            lc += 1
204            #print 'l:%s' % (l[:-1])
205            if len(l) == 0:
206                continue
207            for c in l:
208                #print ']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % (c, ord(c), state, token, macro, map)
209                if c is '#' and not state.startswith('value'):
210                    break
211                if c == '\n' or c == '\r':
212                    if not (state is 'key' and len(token) == 0) and \
213                            not state.startswith('value-multiline'):
214                        raise error.general('malformed macro line:%d: %s' % (lc, l))
215                if state is 'key':
216                    if c not in string.whitespace:
217                        if c is '[':
218                            state = 'map'
219                        elif c is ':':
220                            macro += [token]
221                            token = ''
222                            state = 'attribs'
223                        elif c is '#':
224                            break
225                        else:
226                            token += c
227                elif state is 'map':
228                    if c is ']':
229                        if token not in macros:
230                            macros[token] = {}
231                        map = token
232                        token = ''
233                        state = 'key'
234                    elif c in string.printable and c not in string.whitespace:
235                        token += c
236                    else:
237                        raise error.general('invalid macro map:%d: %s' % (lc, l))
238                elif state is 'attribs':
239                    if c not in string.whitespace:
240                        if c is ',':
241                            macro += [token]
242                            token = ''
243                            if len(macro) == 3:
244                                state = 'value-start'
245                        else:
246                            token += c
247                elif state is 'value-start':
248                    if c is "'":
249                        state = 'value-line-start'
250                elif state is 'value-line-start':
251                    if c is "'":
252                        state = 'value-multiline-start'
253                    else:
254                        state = 'value-line'
255                        token += c
256                elif state is 'value-multiline-start':
257                    if c is "'":
258                        state = 'value-multiline'
259                    else:
260                        macro += [token]
261                        state = 'macro'
262                elif state is 'value-line':
263                    if c is "'":
264                        macro += [token]
265                        state = 'macro'
266                    else:
267                        token += c
268                elif state is 'value-multiline':
269                    if c is "'":
270                        state = 'value-multiline-end'
271                    else:
272                        token += c
273                elif state is 'value-multiline-end':
274                    if c is "'":
275                        state = 'value-multiline-end-end'
276                    else:
277                        state = 'value-multiline'
278                        token += "'" + c
279                elif state is 'value-multiline-end-end':
280                    if c is "'":
281                        macro += [token]
282                        state = 'macro'
283                    else:
284                        state = 'value-multiline'
285                        token += "''" + c
286                else:
287                    raise error.internal('bad state: %s' % (state))
288                if state is 'macro':
289                    macros[map][macro[0].lower()] = (macro[1], macro[2], macro[3])
290                    macro = []
291                    token = ''
292                    state = 'key'
293        for m in macros:
294            if m not in self.macros:
295                self.macros[m] = {}
296            for mm in macros[m]:
297                self.macros[m][mm] = macros[m][mm]
298
299    def load(self, name):
300        try:
301            name = self.expand(name)
302            mc = open(name, 'r')
303        except IOError, err:
304            raise error.general('opening macro file: %s' % (path.host(name)))
305        macros = self.parse(mc)
306        mc.close()
307        self.files += [name]
308
309    def get(self, key):
310        if type(key) is not str:
311            raise TypeError('bad key type: %s' % (type(key)))
312        key = self.key_filter(key)
313        if self.read_map is not 'global'and key in self.macros[self.read_map]:
314            return self.macros[self.read_map][key]
315        if key in self.macros['global']:
316            return self.macros['global'][key]
317        return None
318
319    def get_type(self, key):
320        m = self.get(key)
321        if m is None:
322            return None
323        return m[0]
324
325    def get_attribute(self, key):
326        m = self.get(key)
327        if m is None:
328            return None
329        return m[1]
330
331    def overridden(self, key):
332        return self.get_attribute(key) == 'override'
333
334    def define(self, key, value = '1'):
335        if type(key) is not str:
336            raise TypeError('bad key type: %s' % (type(key)))
337        self.__setitem__(key, ('none', 'none', value))
338
339    def undefine(self, key):
340        if type(key) is not str:
341            raise TypeError('bad key type: %s' % (type(key)))
342        key = self.key_filter(key)
343        for map in self.macros:
344            if key in self.macros[map]:
345                del self.macros[map][key]
346
347    def expand(self, _str):
348        """Simple basic expander of config file macros."""
349        expanded = True
350        while expanded:
351            expanded = False
352            for m in self.macro_filter.findall(_str):
353                name = m[2:-1]
354                macro = self.get(name)
355                if macro is None:
356                    raise error.general('cannot expand default macro: %s in "%s"' %
357                                        (m, _str))
358                _str = _str.replace(m, macro[2])
359                expanded = True
360        return _str
361
362    def find(self, regex):
363        what = re.compile(regex)
364        keys = []
365        for key in self.keys():
366            if what.match(key):
367                keys += [key]
368        return keys
369
370    def set_read_map(self, map):
371        if map in self.macros:
372            self.read_map = map
373            return True
374        return False
375
376    def set_write_map(self, map):
377        if map in self.macros:
378            self.write_map = map
379            return True
380        return False
381
382if __name__ == "__main__":
383    import copy
384    import sys
385    m = macros(name = 'defaults.mc')
386    d = copy.copy(m)
387    m['test1'] = 'something'
388    if d.has_key('test1'):
389        print 'error: copy failed.'
390        sys.exit(1)
391    m.parse("[test]\n" \
392            "test1: none, undefine, ''\n" \
393            "name:  none, override, 'pink'\n")
394    m.set_read_map('test')
395    if m['name'] != 'pink':
396        print 'error: override failed. name is %s' % (m['name'])
397        sys.exit(1)
398    if m.has_key('test1'):
399        print 'error: map undefine failed.'
400        sys.exit(1)
401    print m
402    print m.keys()
Note: See TracBrowser for help on using the repository browser.