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

4.104.114.9
Last change on this file since dbede73 was dbede73, checked in by Chris Johns <chrisj@…>, on Apr 27, 2013 at 10:23:35 AM

Make _sbdir absolute. Add locks to read maps.

Add locks to the read maps to support disabling the select command.

Make the _sbdir etc absolute to let it be referenced from anywhere.

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