source: rtems-tools/rtemstoolkit/macros.py @ 50fdf12

4.104.11
Last change on this file since 50fdf12 was 50fdf12, checked in by Chris Johns <chrisj@…>, on Feb 14, 2014 at 7:30:06 PM

rt: Add the rtems-tester.

  • Property mode set to 100644
File size: 17.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# Macro tables.
33#
34
35import copy
36import re
37import os
38import string
39
40import error
41import path
42
43#
44# Macro tables
45#
46class macros:
47
48    class macro_iterator:
49        def __init__(self, keys):
50            self.keys = keys
51            self.index = 0
52
53        def __iter__(self):
54            return self
55
56        def next(self):
57            if self.index < len(self.keys):
58                key = self.keys[self.index]
59                self.index += 1
60                return key
61            raise StopIteration
62
63        def iterkeys(self):
64            return self.keys
65
66    def __init__(self, name = None, original = None, rtdir = '.'):
67        self.files = []
68        self.macro_filter = re.compile(r'%{[^}]+}')
69        if original is None:
70            self.macros = {}
71            self.read_maps = []
72            self.read_map_locked = False
73            self.write_map = 'global'
74            self.macros['global'] = {}
75            self.macros['global']['nil'] = ('none', 'none', '')
76            self.macros['global']['_cwd'] = ('dir', 'required', path.abspath(os.getcwd()))
77            self.macros['global']['_rtdir'] = ('dir', 'required', path.abspath(rtdir))
78            self.macros['global']['_rttop'] = ('dir', 'required', path.abspath(path.dirname(rtdir)))
79        else:
80            self.macros = {}
81            for m in original.macros:
82                if m not in self.macros:
83                    self.macros[m] = {}
84                for k in original.macros[m]:
85                    self.macros[m][k] = copy.copy(original.macros[m][k])
86            self.read_maps = sorted(copy.copy(original.read_maps))
87            self.read_map_locked = copy.copy(original.read_map_locked)
88            self.write_map = copy.copy(original.write_map)
89        if name is not None:
90            self.load(name)
91
92    def __copy__(self):
93        return macros(original = self)
94
95    def __str__(self):
96        text_len = 80
97        text = ''
98        for f in self.files:
99            text += '> %s%s' % (f, os.linesep)
100        for map in self.macros:
101            if map in self.read_maps:
102                if self.read_map_locked:
103                    rm = 'R'
104                else:
105                    rm = 'r'
106            else:
107                rm = '-'
108            if map == self.write_map:
109                wm = 'w'
110            else:
111                wm = '-'
112            text += '[%s] %s%s%s' % (map, rm, wm, os.linesep)
113            for k in sorted(self.macros[map].keys()):
114                d = self.macros[map][k]
115                text += " %s:%s '%s'%s '%s'%s" % \
116                    (k, ' ' * (20 - len(k)),
117                     d[0], ' ' * (8 - len(d[0])),
118                     d[1], ' ' * (10 - len(d[1])))
119                if len(d[2]) == 0:
120                    text += "''%s" % (os.linesep)
121                else:
122                    if '\n' in d[2]:
123                        text += "'''"
124                    else:
125                        text += "'"
126                indent = False
127                ds = d[2].split('\n')
128                lc = 0
129                for l in ds:
130                    lc += 1
131                    while len(l):
132                        if indent:
133                            text += ' %21s %10s %12s' % (' ', ' ', ' ')
134                        text += l[0:text_len]
135                        l = l[text_len:]
136                        if len(l):
137                            text += ' \\'
138                        elif lc == len(ds):
139                            if len(ds) > 1:
140                                text += "'''"
141                            else:
142                                text += "'"
143                        text += '%s' % (os.linesep)
144                        indent = True
145        return text
146
147    def __iter__(self):
148        return macros.macro_iterator(self.keys())
149
150    def __getitem__(self, key):
151        macro = self.get(key)
152        if macro is None or macro[1] == 'undefine':
153            raise IndexError('key: %s' % (key))
154        return macro[2]
155
156    def __setitem__(self, key, value):
157        if type(key) is not str:
158            raise TypeError('bad key type (want str): %s' % (type(key)))
159        if type(value) is str:
160            value = ('none', 'none', value)
161        if type(value) is not tuple:
162            raise TypeError('bad value type (want tuple): %s' % (type(value)))
163        if len(value) != 3:
164            raise TypeError('bad value tuple (len not 3): %d' % (len(value)))
165        if type(value[0]) is not str:
166            raise TypeError('bad value tuple type field: %s' % (type(value[0])))
167        if type(value[1]) is not str:
168            raise TypeError('bad value tuple attrib field: %s' % (type(value[1])))
169        if type(value[2]) is not str:
170            raise TypeError('bad value tuple value field: %s' % (type(value[2])))
171        if value[0] not in ['none', 'triplet', 'dir', 'file', 'exe']:
172            raise TypeError('bad value tuple (type field): %s' % (value[0]))
173        if value[1] not in ['none', 'optional', 'required',
174                            'override', 'undefine', 'convert']:
175            raise TypeError('bad value tuple (attrib field): %s' % (value[1]))
176        if value[1] == 'convert':
177            value = self.expand(value)
178        self.macros[self.write_map][self.key_filter(key)] = value
179
180    def __delitem__(self, key):
181        self.undefine(key)
182
183    def __contains__(self, key):
184        return self.has_key(key)
185
186    def __len__(self):
187        return len(self.keys())
188
189    def keys(self):
190        keys = self.macros['global'].keys()
191        for rm in self.get_read_maps():
192            for mk in self.macros[rm]:
193                if self.macros[rm][mk][1] == 'undefine':
194                    if mk in keys:
195                        keys.remove(mk)
196                else:
197                    keys.append(mk)
198        return sorted(set(keys))
199
200    def has_key(self, key):
201        if type(key) is not str:
202            raise TypeError('bad key type (want str): %s' % (type(key)))
203        if self.key_filter(key) not in self.keys():
204            return False
205        return True
206
207    def maps(self):
208        return self.macros.keys()
209
210    def get_read_maps(self):
211        return [rm[5:] for rm in self.read_maps]
212
213    def key_filter(self, key):
214        if key.startswith('%{') and key[-1] is '}':
215            key = key[2:-1]
216        return key.lower()
217
218    def parse(self, lines):
219
220        def _clean(l):
221            if '#' in l:
222                l = l[:l.index('#')]
223            if '\r' in l:
224                l = l[:l.index('r')]
225            if '\n' in l:
226                l = l[:l.index('\n')]
227            return l.strip()
228
229        trace_me = False
230        if trace_me:
231            print '[[[[]]]] parsing macros'
232        orig_macros = copy.copy(self.macros)
233        map = 'global'
234        lc = 0
235        state = 'key'
236        token = ''
237        macro = []
238        for l in lines:
239            lc += 1
240            #print 'l:%s' % (l[:-1])
241            if len(l) == 0:
242                continue
243            l_remaining = l
244            for c in l:
245                if trace_me:
246                    print ']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % \
247                        (c, ord(c), state, token, macro, map)
248                l_remaining = l_remaining[1:]
249                if c is '#' and not state.startswith('value'):
250                    break
251                if c == '\n' or c == '\r':
252                    if not (state is 'key' and len(token) == 0) and \
253                            not state.startswith('value-multiline'):
254                        self.macros = orig_macros
255                        raise error.general('malformed macro line:%d: %s' % (lc, l))
256                if state is 'key':
257                    if c not in string.whitespace:
258                        if c is '[':
259                            state = 'map'
260                        elif c is '%':
261                            state = 'directive'
262                        elif c is ':':
263                            macro += [token]
264                            token = ''
265                            state = 'attribs'
266                        elif c is '#':
267                            break
268                        else:
269                            token += c
270                elif state is 'map':
271                    if c is ']':
272                        if token not in self.macros:
273                            self.macros[token] = {}
274                        map = token
275                        token = ''
276                        state = 'key'
277                    elif c in string.printable and c not in string.whitespace:
278                        token += c
279                    else:
280                        self.macros = orig_macros
281                        raise error.general('invalid macro map:%d: %s' % (lc, l))
282                elif state is 'directive':
283                    if c in string.whitespace:
284                        if token == 'include':
285                            self.load(_clean(l_remaining))
286                            token = ''
287                            state = 'key'
288                            break
289                    elif c in string.printable and c not in string.whitespace:
290                        token += c
291                    else:
292                        self.macros = orig_macros
293                        raise error.general('invalid macro directive:%d: %s' % (lc, l))
294                elif state is 'include':
295                    if c is string.whitespace:
296                        if token == 'include':
297                            state = 'include'
298                    elif c in string.printable and c not in string.whitespace:
299                        token += c
300                    else:
301                        self.macros = orig_macros
302                        raise error.general('invalid macro directive:%d: %s' % (lc, l))
303                elif state is 'attribs':
304                    if c not in string.whitespace:
305                        if c is ',':
306                            macro += [token]
307                            token = ''
308                            if len(macro) == 3:
309                                state = 'value-start'
310                        else:
311                            token += c
312                elif state is 'value-start':
313                    if c is "'":
314                        state = 'value-line-start'
315                elif state is 'value-line-start':
316                    if c is "'":
317                        state = 'value-multiline-start'
318                    else:
319                        state = 'value-line'
320                        token += c
321                elif state is 'value-multiline-start':
322                    if c is "'":
323                        state = 'value-multiline'
324                    else:
325                        macro += [token]
326                        state = 'macro'
327                elif state is 'value-line':
328                    if c is "'":
329                        macro += [token]
330                        state = 'macro'
331                    else:
332                        token += c
333                elif state is 'value-multiline':
334                    if c is "'":
335                        state = 'value-multiline-end'
336                    else:
337                        token += c
338                elif state is 'value-multiline-end':
339                    if c is "'":
340                        state = 'value-multiline-end-end'
341                    else:
342                        state = 'value-multiline'
343                        token += "'" + c
344                elif state is 'value-multiline-end-end':
345                    if c is "'":
346                        macro += [token]
347                        state = 'macro'
348                    else:
349                        state = 'value-multiline'
350                        token += "''" + c
351                else:
352                    self.macros = orig_macros
353                    raise error.internal('bad state: %s' % (state))
354                if state is 'macro':
355                    self.macros[map][macro[0].lower()] = (macro[1], macro[2], macro[3])
356                    macro = []
357                    token = ''
358                    state = 'key'
359
360    def load(self, name):
361        names = self.expand(name).split(':')
362        for n in names:
363            if path.exists(n):
364                try:
365                    mc = open(path.host(n), 'r')
366                    macros = self.parse(mc)
367                    mc.close()
368                    self.files += [n]
369                    return
370                except IOError, err:
371                    pass
372        raise error.general('opening macro file: %s' % \
373                                (path.host(self.expand(name))))
374
375    def get(self, key):
376        if type(key) is not str:
377            raise TypeError('bad key type: %s' % (type(key)))
378        key = self.key_filter(key)
379        for rm in self.get_read_maps():
380            if key in self.macros[rm]:
381                return self.macros[rm][key]
382        if key in self.macros['global']:
383            return self.macros['global'][key]
384        return None
385
386    def get_type(self, key):
387        m = self.get(key)
388        if m is None:
389            return None
390        return m[0]
391
392    def get_attribute(self, key):
393        m = self.get(key)
394        if m is None:
395            return None
396        return m[1]
397
398    def get_value(self, key):
399        m = self.get(key)
400        if m is None:
401            return None
402        return m[2]
403
404    def overridden(self, key):
405        return self.get_attribute(key) == 'override'
406
407    def define(self, key, value = '1'):
408        if type(key) is not str:
409            raise TypeError('bad key type: %s' % (type(key)))
410        self.__setitem__(key, ('none', 'none', value))
411
412    def undefine(self, key):
413        if type(key) is not str:
414            raise TypeError('bad key type: %s' % (type(key)))
415        key = self.key_filter(key)
416        for map in self.macros:
417            if key in self.macros[map]:
418                del self.macros[map][key]
419
420    def expand(self, _str):
421        """Simple basic expander of config file macros."""
422        expanded = True
423        while expanded:
424            expanded = False
425            for m in self.macro_filter.findall(_str):
426                name = m[2:-1]
427                macro = self.get(name)
428                if macro is None:
429                    print self.macros
430                    raise error.general('cannot expand default macro: %s in "%s"' %
431                                        (m, _str))
432                _str = _str.replace(m, macro[2])
433                expanded = True
434        return _str
435
436    def find(self, regex):
437        what = re.compile(regex)
438        keys = []
439        for key in self.keys():
440            if what.match(key):
441                keys += [key]
442        return keys
443
444    def set_read_map(self, _map):
445        if not self.read_map_locked:
446            if _map in self.macros:
447                if _map not in self.get_read_maps():
448                    rm = '%04d_%s' % (len(self.read_maps), _map)
449                    self.read_maps = sorted(self.read_maps + [rm])
450                return True
451        return False
452
453    def unset_read_map(self, _map):
454        if not self.read_map_locked:
455            if _map in self.get_read_maps():
456                for i in range(0, len(self.read_maps)):
457                    if '%04d_%s' % (i, _map) == self.read_maps[i]:
458                        self.read_maps.pop(i)
459                return True
460        return False
461
462    def set_write_map(self, map):
463        if map in self.macros:
464            self.write_map = map
465            return True
466        return False
467
468    def lock_read_map(self):
469        self.read_map_locked = True
470
471    def unlock_read_map(self):
472        self.read_map_locked = False
473
474if __name__ == "__main__":
475    import copy
476    import sys
477    m = macros(name = 'defaults.mc')
478    d = copy.copy(m)
479    m['test1'] = 'something'
480    if d.has_key('test1'):
481        print 'error: copy failed.'
482        sys.exit(1)
483    m.parse("[test]\n" \
484            "test1: none, undefine, ''\n" \
485            "name:  none, override, 'pink'\n")
486    print 'set test:', m.set_read_map('test')
487    if m['name'] != 'pink':
488        print 'error: override failed. name is %s' % (m['name'])
489        sys.exit(1)
490    if m.has_key('test1'):
491        print 'error: map undefine failed.'
492        sys.exit(1)
493    print 'unset test:', m.unset_read_map('test')
494    print m
495    print m.keys()
Note: See TracBrowser for help on using the repository browser.