source: rtems-source-builder/source-builder/sb/macros.py @ 624954b

4.104.114.9
Last change on this file since 624954b was 624954b, checked in by Chris Johns <chrisj@…>, on Apr 26, 2013 at 2:05:53 AM

Add macro include support. Use it for building from head.

This change provides a simple way to build all parts of the tools from
version control.

  • Property mode set to 100644
File size: 15.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_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
190        def _clean(l):
191            if '#' in l:
192                l = l[:l.index('#')]
193            if '\r' in l:
194                l = l[:l.index('r')]
195            if '\n' in l:
196                l = l[:l.index('\n')]
197            return l.strip()
198
199        trace_me = False
200        if trace_me:
201            print '[[[[]]]] parsing macros'
202        macros = { 'global': {} }
203        map = 'global'
204        lc = 0
205        state = 'key'
206        token = ''
207        macro = []
208        for l in lines:
209            lc += 1
210            #print 'l:%s' % (l[:-1])
211            if len(l) == 0:
212                continue
213            l_remaining = l
214            for c in l:
215                if trace_me:
216                    print ']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % \
217                        (c, ord(c), state, token, macro, map)
218                l_remaining = l_remaining[1:]
219                if c is '#' and not state.startswith('value'):
220                    break
221                if c == '\n' or c == '\r':
222                    if not (state is 'key' and len(token) == 0) and \
223                            not state.startswith('value-multiline'):
224                        raise error.general('malformed macro line:%d: %s' % (lc, l))
225                if state is 'key':
226                    if c not in string.whitespace:
227                        if c is '[':
228                            state = 'map'
229                        elif c is '%':
230                            state = 'directive'
231                        elif c is ':':
232                            macro += [token]
233                            token = ''
234                            state = 'attribs'
235                        elif c is '#':
236                            break
237                        else:
238                            token += c
239                elif state is 'map':
240                    if c is ']':
241                        if token not in macros:
242                            macros[token] = {}
243                        map = token
244                        token = ''
245                        state = 'key'
246                    elif c in string.printable and c not in string.whitespace:
247                        token += c
248                    else:
249                        raise error.general('invalid macro map:%d: %s' % (lc, l))
250                elif state is 'directive':
251                    if c in string.whitespace:
252                        if token == 'include':
253                            self.load(_clean(l_remaining))
254                            token = ''
255                            state = 'key'
256                            break
257                    elif c in string.printable and c not in string.whitespace:
258                        token += c
259                    else:
260                        raise error.general('invalid macro directive:%d: %s' % (lc, l))
261                elif state is 'include':
262                    if c is string.whitespace:
263                        if token == 'include':
264                            state = 'include'
265                    elif c in string.printable and c not in string.whitespace:
266                        token += c
267                    else:
268                        raise error.general('invalid macro directive:%d: %s' % (lc, l))
269                elif state is 'attribs':
270                    if c not in string.whitespace:
271                        if c is ',':
272                            macro += [token]
273                            token = ''
274                            if len(macro) == 3:
275                                state = 'value-start'
276                        else:
277                            token += c
278                elif state is 'value-start':
279                    if c is "'":
280                        state = 'value-line-start'
281                elif state is 'value-line-start':
282                    if c is "'":
283                        state = 'value-multiline-start'
284                    else:
285                        state = 'value-line'
286                        token += c
287                elif state is 'value-multiline-start':
288                    if c is "'":
289                        state = 'value-multiline'
290                    else:
291                        macro += [token]
292                        state = 'macro'
293                elif state is 'value-line':
294                    if c is "'":
295                        macro += [token]
296                        state = 'macro'
297                    else:
298                        token += c
299                elif state is 'value-multiline':
300                    if c is "'":
301                        state = 'value-multiline-end'
302                    else:
303                        token += c
304                elif state is 'value-multiline-end':
305                    if c is "'":
306                        state = 'value-multiline-end-end'
307                    else:
308                        state = 'value-multiline'
309                        token += "'" + c
310                elif state is 'value-multiline-end-end':
311                    if c is "'":
312                        macro += [token]
313                        state = 'macro'
314                    else:
315                        state = 'value-multiline'
316                        token += "''" + c
317                else:
318                    raise error.internal('bad state: %s' % (state))
319                if state is 'macro':
320                    macros[map][macro[0].lower()] = (macro[1], macro[2], macro[3])
321                    macro = []
322                    token = ''
323                    state = 'key'
324        for m in macros:
325            if m not in self.macros:
326                self.macros[m] = {}
327            for mm in macros[m]:
328                self.macros[m][mm] = macros[m][mm]
329
330    def load(self, name):
331        names = self.expand(name).split(':')
332        for n in names:
333            if path.exists(n):
334                try:
335                    mc = open(n, 'r')
336                    macros = self.parse(mc)
337                    mc.close()
338                    self.files += [n]
339                    return
340                except IOError, err:
341                    pass
342        raise error.general('opening macro file: %s' % (path.host(name)))
343
344    def get(self, key):
345        if type(key) is not str:
346            raise TypeError('bad key type: %s' % (type(key)))
347        key = self.key_filter(key)
348        for rm in self.get_read_maps():
349            if key in self.macros[rm]:
350                return self.macros[rm][key]
351        if key in self.macros['global']:
352            return self.macros['global'][key]
353        return None
354
355    def get_type(self, key):
356        m = self.get(key)
357        if m is None:
358            return None
359        return m[0]
360
361    def get_attribute(self, key):
362        m = self.get(key)
363        if m is None:
364            return None
365        return m[1]
366
367    def overridden(self, key):
368        return self.get_attribute(key) == 'override'
369
370    def define(self, key, value = '1'):
371        if type(key) is not str:
372            raise TypeError('bad key type: %s' % (type(key)))
373        self.__setitem__(key, ('none', 'none', value))
374
375    def undefine(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 map in self.macros:
380            if key in self.macros[map]:
381                del self.macros[map][key]
382
383    def expand(self, _str):
384        """Simple basic expander of config file macros."""
385        expanded = True
386        while expanded:
387            expanded = False
388            for m in self.macro_filter.findall(_str):
389                name = m[2:-1]
390                macro = self.get(name)
391                if macro is None:
392                    raise error.general('cannot expand default macro: %s in "%s"' %
393                                        (m, _str))
394                _str = _str.replace(m, macro[2])
395                expanded = True
396        return _str
397
398    def find(self, regex):
399        what = re.compile(regex)
400        keys = []
401        for key in self.keys():
402            if what.match(key):
403                keys += [key]
404        return keys
405
406    def set_read_map(self, _map):
407        if _map in self.macros:
408            if _map not in self.get_read_maps():
409                rm = '%04d_%s' % (len(self.read_maps), _map)
410                self.read_maps = sorted(self.read_maps + [rm])
411            return True
412        return False
413
414    def unset_read_map(self, _map):
415        if _map in self.get_read_maps():
416            for i in range(0, len(self.read_maps)):
417                if '%04d_%s' % (i, _map) == self.read_maps[i]:
418                    self.read_maps.pop(i)
419            return True
420        return False
421
422    def set_write_map(self, map):
423        if map in self.macros:
424            self.write_map = map
425            return True
426        return False
427
428if __name__ == "__main__":
429    import copy
430    import sys
431    m = macros(name = 'defaults.mc')
432    d = copy.copy(m)
433    m['test1'] = 'something'
434    if d.has_key('test1'):
435        print 'error: copy failed.'
436        sys.exit(1)
437    m.parse("[test]\n" \
438            "test1: none, undefine, ''\n" \
439            "name:  none, override, 'pink'\n")
440    print 'set test:', m.set_read_map('test')
441    if m['name'] != 'pink':
442        print 'error: override failed. name is %s' % (m['name'])
443        sys.exit(1)
444    if m.has_key('test1'):
445        print 'error: map undefine failed.'
446        sys.exit(1)
447    print 'unset test:', m.unset_read_map('test')
448    print m
449    print m.keys()
Note: See TracBrowser for help on using the repository browser.