source: rtems-source-builder/source-builder/sb/macros.py @ 76188ee4

4.11
Last change on this file since 76188ee4 was 2e54abc9, checked in by Chris Johns <chrisj@…>, on 03/26/15 at 00:12:13

sb: Make the source and patch labels have sortable label.

This patch changes 'patch1' to 'patch0001' so sorting of the macro
keys in the patch macro map returns the order the patches are declared.
The helps mamage patch sets that need a specific order.

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