source: rtems-tools/rtemstoolkit/macros.py @ b7d48ef

4.104.11
Last change on this file since b7d48ef was b7d48ef, checked in by Chris Johns <chrisj@…>, on Feb 8, 2015 at 6:12:04 AM

Install the rtems-test command.

This installs the Python RTEMS Toolkit.

The copmiler has been switched from forcing gcc to allowing waf
to detect the host's tool chain.

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