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

4.104.114.9
Last change on this file since fd5042a was fd5042a, checked in by Chris Johns <chrisj@…>, on Apr 20, 2013 at 11:42:39 AM

Support multiple read maps.

A build could require more than one map to be active at once. This
change allows more than one map to be set. An example is gcc and
newlib. Having separate maps allows a user to control which part
they test.

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