source: rtems-source-builder/source-builder/sb/reports.py @ f88fcf3

4.11
Last change on this file since f88fcf3 was f88fcf3, checked in by Chris Johns <chrisj@…>, on Mar 7, 2016 at 12:56:02 AM

sb: Update code base to support Python3 and Python2.

Fix Windows support to allow MSYS2 Python to be used.

Updates #2619.

  • Property mode set to 100644
File size: 28.4 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2015 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# This code builds a package given a config file. It only builds to be
22# installed not to be package unless you run a packager around this.
23#
24
25from __future__ import print_function
26
27import copy
28import datetime
29import os
30import sys
31
32import pprint
33pp = pprint.PrettyPrinter(indent = 2)
34
35try:
36    import build
37    import check
38    import config
39    import error
40    import git
41    import log
42    import options
43    import path
44    import setbuilder
45    import sources
46    import version
47except KeyboardInterrupt:
48    print('user terminated', file = sys.stderr)
49    sys.exit(1)
50except:
51    print('error: unknown application load error', file = sys.stderr)
52    sys.exit(1)
53
54_line_len = 78
55
56_title = 'RTEMS Tools Project <users@rtems.org>'
57
58_release_status_text = 'RTEMS Source Builder Release'
59_git_status_text = 'RTEMS Source Builder Repository Status'
60
61def _make_path(p, *args):
62    for arg in args:
63        p = path.join(p, arg)
64    return os.path.abspath(path.host(p))
65
66class formatter(object):
67    def __init__(self):
68        self.content = ''
69
70    def line(self, text):
71        self.content += text + '\n'
72
73    def add(self, text):
74        self.content += text
75
76    def set_sbpath(self, sbpath):
77        self.sbpath = sbpath
78
79    def format(self):
80        raise error.general('internal error: formatter.format() not implemented')
81
82    def ext(self):
83        raise error.general('internal error: formatter.ext() not implemented')
84
85    def introduction(self, name, now, intro_text):
86        self.line('=' * _line_len)
87        self.line('%s %s' % (_title, now))
88        if intro_text:
89            self.line('')
90            self.line('%s' % ('\n'.join(intro_text)))
91        self.line('=' * _line_len)
92        self.line('Report: %s' % (name))
93
94    def epilogue(self, name):
95        return
96
97    def config_start(self, nest_level, name):
98        return
99
100    def config(self, nest_level, name, _config):
101        self.line('-' * _line_len)
102        self.line('Package: %s' % (name))
103        self.line(' Config: %s' % (_config.file_name()))
104
105    def config_end(self, nest_level, name):
106        return
107
108    def buildset_start(self, nest_level, name):
109        self.line('=-' * int(_line_len / 2))
110        self.line('Build Set: (%d) %s' % (nest_level, name))
111
112    def buildset_end(self, nest_level, name):
113        return
114
115    def info(self, nest_level, name, info, separated):
116        self.line(' ' + name + ':')
117        for l in info:
118            self.line('  ' + l)
119
120    def directive(self, nest_level, name, data):
121        self.line(' %s:' % (name))
122        for l in data:
123            self.line('  ' + l)
124
125    def files(self, nest_level, sigular, plural, _files):
126        self.line('  ' + plural + ': %d' % (len(_files)))
127        i = 0
128        for name in _files:
129            for s in _files[name]:
130                i += 1
131                self.line('   %2d: %s' % (i, s[0]))
132                if s[1] is None:
133                    h = 'No checksum'
134                else:
135                    hash = s[1].split()
136                    h = '%s: %s' % (hash[0], hash[1])
137                self.line('       %s' % (h))
138
139    def post_process(self):
140        return self.content
141
142class asciidoc_formatter(formatter):
143    def __init__(self):
144        super(asciidoc_formatter, self).__init__()
145
146    def format(self):
147        return 'asciidoc'
148
149    def ext(self):
150        return '.txt'
151
152    def introduction(self, name, now, intro_text):
153        h = 'RTEMS Source Builder Report'
154        self.line(h)
155        self.line('=' * len(h))
156        self.line(':doctype: book')
157        self.line(':toc2:')
158        self.line(':toclevels: 5')
159        self.line(':icons:')
160        self.line(':numbered:')
161        self.line(':data-uri:')
162        self.line('')
163        self.line(_title)
164        self.line(now)
165        self.line('')
166        image = _make_path(self.sbpath, options.basepath, 'images', 'rtemswhitebg.jpg')
167        self.line('image:%s["RTEMS",width="20%%"]' % (image))
168        self.line('')
169        if intro_text:
170            self.line('%s' % ('\n'.join(intro_text)))
171
172    def release_status(self, release_string):
173        self.line('')
174        self.line("'''")
175        self.line('')
176        self.line('.%s' % (_release_status_text))
177        self.line('*Version*: %s;;' % (release_string))
178        self.line('')
179        self.line("'''")
180        self.line('')
181
182    def git_status(self, valid, dirty, head, remotes):
183        self.line('')
184        self.line("'''")
185        self.line('')
186        self.line('.%s' % (_git_status_text))
187        if valid:
188            self.line('*Remotes*:;;')
189            for r in remotes:
190                if 'url' in remotes[r]:
191                    text = remotes[r]['url']
192                else:
193                    text = 'no URL found'
194                text = '%s: %s' % (r, text)
195                self.line('. %s' % (text))
196            self.line('*Status*:;;')
197            if dirty:
198                self.line('_Repository is dirty_')
199            else:
200                self.line('Clean')
201            self.line('*Head*:;;')
202            self.line('Commit: %s' % (head))
203        else:
204            self.line('_Not a valid GIT repository_')
205        self.line('')
206        self.line("'''")
207        self.line('')
208
209    def config(self, nest_level, name, _config):
210        self.line('*Package*: _%s_ +' % (name))
211        self.line('*Config*: %s' % (_config.file_name()))
212        self.line('')
213
214    def config_end(self, nest_level, name):
215        self.line('')
216        self.line("'''")
217        self.line('')
218
219    def buildset_start(self, nest_level, name):
220        h = '%s' % (name)
221        self.line('=%s %s' % ('=' * int(nest_level), h))
222
223    def info(self, nest_level, name, info, separated):
224        end = ''
225        if separated:
226            self.line('*%s:*::' % (name))
227            self.line('')
228        else:
229            self.line('*%s:* ' % (name))
230            end = ' +'
231        spaces = ''
232        for l in info:
233            self.line('%s%s%s' % (spaces, l, end))
234        if separated:
235            self.line('')
236
237    def directive(self, nest_level, name, data):
238        self.line('')
239        self.line('*%s*:' % (name))
240        self.line('--------------------------------------------')
241        for l in data:
242            self.line(l)
243        self.line('--------------------------------------------')
244
245    def files(self, nest_level, singular, plural, _files):
246        self.line('')
247        self.line('*' + plural + ':*::')
248        if len(_files) == 0:
249            self.line('No ' + plural.lower())
250        for name in _files:
251            for s in _files[name]:
252                self.line('. %s' % (s[0]))
253                if s[1] is None:
254                    h = 'No checksum'
255                else:
256                    hash = s[1].split()
257                    h = '%s: %s' % (hash[0], hash[1])
258                self.line('+\n%s\n' % (h))
259
260class html_formatter(asciidoc_formatter):
261    def __init__(self):
262        super(html_formatter, self).__init__()
263
264    def format(self):
265        return 'html'
266
267    def ext(self):
268        return '.html'
269
270    def post_process(self):
271        import io
272        infile = io.StringIO(self.content)
273        outfile = io.StringIO()
274        try:
275            import asciidocapi
276        except:
277            raise error.general('installation error: no asciidocapi found')
278        asciidoc_py = _make_path(self.sbpath, options.basepath, 'asciidoc', 'asciidoc.py')
279        try:
280            asciidoc = asciidocapi.AsciiDocAPI(asciidoc_py)
281        except:
282            raise error.general('application error: asciidocapi failed')
283        asciidoc.execute(infile, outfile)
284        out = outfile.getvalue()
285        infile.close()
286        outfile.close()
287        return out
288
289class text_formatter(formatter):
290    def __init__(self):
291        super(text_formatter, self).__init__()
292        self.cini = ''
293
294    def format(self):
295        return 'text'
296
297    def ext(self):
298        return '.txt'
299
300    def introduction(self, name, now, intro_text):
301        self.line('=' * _line_len)
302        self.line('%s %s' % (_title, now))
303        if intro_text:
304            self.line('')
305            self.line('%s' % ('\n'.join(intro_text)))
306        self.line('=' * _line_len)
307        self.line('Report: %s' % (name))
308
309    def release_status_header(self):
310        self.line('-' * _line_len)
311        self.line('%s' % (_release_status_text))
312
313    def release_status(self, release_string):
314        self.release_status_header()
315        self.line('%s Version: %s' % (self.cini, release_string))
316
317    def git_status_header(self):
318        self.line('-' * _line_len)
319        self.line('%s' % (_git_status_text))
320
321    def git_status(self, valid, dirty, head, remotes):
322        self.git_status_header()
323        if valid:
324            self.line('%s Remotes:' % (self.cini))
325            rc = 0
326            for r in remotes:
327                rc += 1
328                if 'url' in remotes[r]:
329                    text = remotes[r]['url']
330                else:
331                    text = 'no URL found'
332                text = '%s: %s' % (r, text)
333                self.line('%s  %2d: %s' % (self.cini, rc, text))
334            self.line('%s Status:' % (self.cini))
335            if dirty:
336                self.line('%s  Repository is dirty' % (self.cini))
337            else:
338                self.line('%s  Clean' % (self.cini))
339            self.line('%s Head:' % (self.cini))
340            self.line('%s  Commit: %s' % (self.cini, head))
341        else:
342            self.line('%s Not a valid GIT repository' % (self.cini))
343
344class ini_formatter(text_formatter):
345    def __init__(self):
346        super(ini_formatter, self).__init__()
347        self.cini = ';'
348        self.ini_pkg = {}
349        self.name = None
350
351    def format(self):
352        return 'ini'
353
354    def ext(self):
355        return '.ini'
356
357    def introduction(self, name, now, intro_text):
358        self.line(';')
359        self.line('; %s %s' % (_title, now))
360        if intro_text:
361            self.line(';')
362            self.line('; %s' % ('\n; '.join(intro_text)))
363            self.line(';')
364
365    def epilogue(self, name):
366        pkgs = sorted(self.ini_pkg.keys())
367        for pkg in pkgs:
368            self.line('')
369            self.line('[%s]' % (pkg))
370            items = sorted(self.ini_pkg[pkg].keys())
371            for item in items:
372                i = self.ini_pkg[pkg][item]
373                if len(i) == 1:
374                    self.line('%s = "%s"' % (item, i[0]))
375                else:
376                    self.line('%s = <<<DATA' % (item))
377                    self.line('\n'.join(i))
378                    self.line('DATA')
379        self.line('')
380
381    def release_status_header(self):
382        self.line(';')
383        self.line('; %s' % (_release_status_text))
384
385    def git_status_header(self):
386        self.line(';')
387        self.line('; %s' % (_git_status_text))
388        self.line(';')
389
390    def config(self, nest_level, name, _config):
391        pass
392
393    def buildset_start(self, nest_level, name):
394        if name.endswith('.cfg'):
395            self.name = path.basename(name[:-4])
396            if self.name not in self.ini_pkg:
397                self.ini_pkg[self.name] = {}
398
399    def buildset_end(self, nest_level, name):
400        self.name = None
401
402    def info(self, nest_level, name, info, separated):
403        if self.name:
404            if 'info' not in self.ini_pkg[self.name]:
405                self.ini_pkg[self.name]['info'] = []
406            self.ini_pkg[self.name]['info'] += info
407
408    def directive(self, nest_level, name, data):
409        if self.name:
410            if name not in self.ini_pkg[self.name]:
411                self.ini_pkg[self.name][name] = []
412            self.ini_pkg[self.name][name] += data
413
414    def files(self, nest_level, singular, plural, _files):
415        pass
416
417class xml_formatter(formatter):
418    def __init__(self):
419        super(xml_formatter, self).__init__()
420
421    def format(self):
422        return 'xml'
423
424    def ext(self):
425        return '.xml'
426
427    def introduction(self, name, now, intro_text):
428        self.line('<RTEMSSourceBuilderReport>')
429        if intro_text:
430            self.line('\t<Introduction>%s</Introduction>' % (intro_text))
431
432    def epilogue(self, name):
433        self.line('</RTEMSSourceBuilderReport>')
434
435    def release_status(self, release_string):
436        self.line('\t<Release>')
437        self.line('\t\t<Version>%s</Version>' % (release_string))
438        self.line('\t</Release>')
439
440    def git_status(self, valid, dirty, head, remotes):
441        self.line('\t<Git>')
442        if valid:
443            if dirty:
444                self.line('\t\t<Status>dirty</Status>')
445            else:
446                self.line('\t\t<Status>clean</Status>')
447            self.line('\t\t<Commit>' + head + '</Commit>')
448        else:
449            self.line('\t\t<Status>invalid</Status>')
450        self.line('\t</Git>')
451
452    def config_start(self, nest_level, name):
453        self.line('\t' * nest_level + '<Package name="' + name + '">')
454
455    def config(self, nest_level, name, _config):
456        self.line('\t' * nest_level + '<Config>' + _config.file_name() + '</Config>')
457
458    def config_end(self, nest_level, name):
459        self.line('\t' * nest_level + '</Package>')
460
461    def buildset_start(self, nest_level, name):
462        self.line('\t' * nest_level + '<BuildSet name="' + name + '">')
463
464    def buildset_end(self, nest_level, name):
465        self.line('\t' * nest_level + '</BuildSet>')
466
467    def info(self, nest_level, name, info, separated):
468        self.add('\t' * nest_level + '<' + name.replace(' ', '') + '>')
469        for l in info:
470            self.add(l)
471        self.line('</' + name + '>')
472
473    def directive(self, nest_level, name, data):
474        self.line('\t' * nest_level + '<' + name + '><![CDATA[')
475        for l in data:
476            self.line(l.replace(']]>', ']]]><![CDATA[]>'))
477        self.line(']]></' + name + '>')
478
479    def files(self, nest_level, sigular, plural, _files):
480        for name in _files:
481            for s in _files[name]:
482                self.add('\t' * nest_level + '<' + sigular)
483                if not (s[1] is None):
484                    hash = s[1].split()
485                    self.add(' ' + hash[0] + '="' + hash[1] + '"')
486                self.line('>' + s[0] + '</' + sigular + '>')
487
488def _tree_name(path_):
489    return path.splitext(path.basename(path_))[0]
490
491def _merge(_dict, new):
492    new = copy.deepcopy(new)
493    for i in new:
494        if i not in _dict:
495            _dict[i] = new[i]
496        else:
497            _dict[i] += new[i]
498
499class report:
500    """Report the build details about a package given a config file."""
501
502    def __init__(self, formatter, _configs, opts, macros = None):
503        if type(formatter) == str:
504            if formatter == 'text':
505                self.formatter = text_formatter()
506            elif formatter == 'asciidoc':
507                self.formatter = asciidoc_formatter()
508            elif formatter == 'html':
509                self.formatter = html_formatter()
510            elif formatter == 'ini':
511                self.formatter = ini_formatter()
512            elif formatter == 'xml':
513                self.formatter = xml_formatter()
514            else:
515                raise error.general('invalid format: %s' % (formatter))
516        else:
517            self.formatter = formatter
518        self.configs = _configs
519        self.opts = opts
520        if macros is None:
521            self.macros = opts.defaults
522        else:
523            self.macros = macros
524        self.sbpath = self.macros.expand('%{_sbdir}')
525        self.formatter.set_sbpath(self.sbpath)
526        self.bset_nesting = 0
527        self.out = ''
528        self.tree = {}
529        self.files = { 'buildsets':[], 'configs':[] }
530
531    def output(self, text):
532        self.formatter.line(text)
533
534    def is_ini(self):
535        return self.formatter.format() == 'ini'
536
537    def header(self):
538        pass
539
540    def footer(self):
541        pass
542
543    def release_status(self):
544        self.formatter.release_status(version.str())
545
546    def git_status(self):
547        r = git.repo('.', self.opts, self.macros)
548        self.formatter.git_status(r.valid(), r.dirty(), r.head(), r.remotes())
549
550    def introduction(self, name, intro_text = None):
551        now = datetime.datetime.now().ctime()
552        self.formatter.introduction(name, now, intro_text)
553        if version.released():
554            self.release_status()
555        else:
556            self.git_status()
557
558    def epilogue(self, name):
559        self.formatter.epilogue(name)
560
561    def config_start(self, name, _config):
562        self.files['configs'] += [name]
563        for cf in _config.includes():
564            cfbn = path.basename(cf)
565            if cfbn not in self.files['configs']:
566                self.files['configs'] += [cfbn]
567        self.formatter.config_start(self.bset_nesting + 1, name)
568
569    def config_end(self, name, _config):
570        self.formatter.config_end(self.bset_nesting + 1, name)
571
572    def buildset_start(self, name):
573        self.bset_nesting += 1
574        self.files['buildsets'] += [name]
575        self.formatter.buildset_start(self.bset_nesting, name)
576
577    def buildset_end(self, name):
578        self.formatter.buildset_end(self.bset_nesting, name)
579        self.bset_nesting -= 1
580
581    def source(self, macros):
582        def err(msg):
583            raise error.general('%s' % (msg))
584        _srcs = {}
585        for p in sources.get_source_names(macros, err):
586            if 'setup' in sources.get_source_keys(p, macros, err):
587                _srcs[p] = \
588                    [s for s in sources.get_sources(p, macros, err) if not s.startswith('%setup')]
589                _srcs[p] = [macros.expand(s) for s in _srcs[p]]
590        srcs = {}
591        for p in _srcs:
592            srcs[p] = [(s, sources.get_hash(path.basename(s).lower(), macros)) for s in _srcs[p]]
593        return srcs
594
595    def patch(self, macros):
596        def err(msg):
597            raise error.general('%s' % (msg))
598        _patches = {}
599        for n in sources.get_patch_names(macros, err):
600            if 'setup' in sources.get_patch_keys(n, macros, err):
601                _patches[n] = \
602                    [p for p in sources.get_patches(n, macros, err) if not p.startswith('%setup')]
603                _patches[n] = [macros.expand(p.split()[-1]) for p in _patches[n]]
604        patches = {}
605        for n in _patches:
606            patches[n] = [(p, sources.get_hash(path.basename(p).lower(), macros)) for p in _patches[n]]
607        return patches
608
609    def output_info(self, name, info, separated = False):
610        if info is not None:
611            self.formatter.info(self.bset_nesting + 2, name, info, separated)
612
613    def output_directive(self, name, directive):
614        if directive is not None:
615            self.formatter.directive(self.bset_nesting + 2, name, directive)
616
617    def tree_packages(self, tree, packages = []):
618        if 'bset' in tree:
619            for node in sorted(tree['bset'].keys()):
620                packages += [_tree_name(node)]
621                packages += self.tree_packages(tree['bset'][node], packages)
622        return set(packages)
623
624    def tree_sources(self, name, tree, sources = []):
625        if 'cfg' in tree:
626            packages = {}
627            if 'sources' in tree['cfg']:
628                _merge(packages, tree['cfg']['sources'])
629            if 'patches' in tree['cfg']:
630                _merge(packages, tree['cfg']['patches'])
631            for package in packages:
632                for source in packages[package]:
633                    if not source[0].startswith('git') and not source[0].startswith('cvs'):
634                        sources += [(path.basename(source[0]), source[0], source[1])]
635        if 'bset' in tree:
636            for node in sorted(tree['bset'].keys()):
637                self.tree_sources(_tree_name(node), tree['bset'][node], sources)
638        return sources
639
640    def config(self, _config, tree, opts, macros):
641        packages = _config.packages()
642        package = packages['main']
643        name = package.name()
644        if len(name) == 0:
645            return
646        sources = self.source(macros)
647        patches = self.patch(macros)
648        if tree is not None:
649            tree['file'] += [_config.file_name()]
650            if len(sources):
651                if 'sources' in tree:
652                    tree['sources'] = dict(list(tree['sources'].items()) + list(sources.items()))
653                else:
654                    tree['sources'] = sources
655            if len(patches):
656                if 'patches' in tree:
657                    tree['patches'] = dict(list(tree['patches'].items()) + list(patches.items()))
658                else:
659                    tree['patches'] = patches
660        self.config_start(name, _config)
661        self.formatter.config(self.bset_nesting + 2, name, _config)
662        self.output_info('Summary', package.get_info('summary'), True)
663        self.output_info('URL', package.get_info('url'))
664        self.output_info('Version', package.get_info('version'))
665        self.output_info('Release', package.get_info('release'))
666        self.output_info('Build Arch', package.get_info('buildarch'))
667        self.formatter.files(self.bset_nesting + 2, "Source", "Sources", sources)
668        self.formatter.files(self.bset_nesting + 2, "Patch", "Patches", patches)
669        self.output_directive('Preparation', package.prep())
670        self.output_directive('Build', package.build())
671        self.output_directive('Install', package.install())
672        self.output_directive('Clean', package.clean())
673        self.config_end(name, _config)
674
675    def generate_ini_tree(self, name, tree, prefix_char, prefix = ''):
676        if prefix_char == '|':
677            c = '|'
678        else:
679            c = '+'
680        self.output('; %s  %s- %s' % (prefix, c, name))
681        prefix += %s ' % (prefix_char)
682        if 'cfg' in tree:
683            files = sorted(tree['cfg']['file'])
684            if len(files):
685                for f in range(0, len(files) - 1):
686                    self.output('; %s  |- %s' % (prefix, files[f]))
687                if 'bset' in tree and len(list(tree['bset'].keys())):
688                    c = '|'
689                else:
690                    c = '+'
691                self.output('; %s  %s- %s' % (prefix, c, files[f + 1]))
692        if 'bset' in tree:
693            nodes = sorted(tree['bset'].keys())
694            for node in range(0, len(nodes)):
695                if node == len(nodes) - 1:
696                    prefix_char = ' '
697                else:
698                    prefix_char = '|'
699                self.generate_ini_tree(nodes[node],
700                                       tree['bset'][nodes[node]],
701                                       prefix_char,
702                                       prefix)
703
704    def generate_ini_source(self, sources):
705        self.output('')
706        self.output('[source]')
707        for source in sources:
708            self.output(' %s = %s' % (source[0], source[1]))
709
710    def generate_ini_hash(self, sources):
711        self.output('')
712        self.output('[hash]')
713        for source in sources:
714            if source[2] is None:
715                hash = ''
716            else:
717                hash = source[2].split()
718                hash = '%s:%s' % (hash[0], hash[1])
719            self.output(' %s = %s' % (source[0], hash))
720
721    def generate_ini(self):
722        nodes = sorted([node for node in list(self.tree.keys()) if node != 'bset'])
723        self.output(';')
724        self.output('; Configuration Tree:')
725        for node in range(0, len(nodes)):
726            if node == len(nodes) - 1:
727                prefix_char = ' '
728            else:
729                prefix_char = '|'
730            self.generate_ini_tree(nodes[node], self.tree[nodes[node]], prefix_char)
731        self.output(';')
732        sources = []
733        for node in nodes:
734            sources += self.tree_sources(_tree_name(node), self.tree[node])
735        sources = sorted(set(sources))
736        self.generate_ini_source(sources)
737        self.generate_ini_hash(sources)
738
739    def write(self, name):
740        self.out = self.formatter.post_process()
741        if name is not None:
742            try:
743                o = open(path.host(name), "w")
744                o.write(self.out)
745                o.close()
746                del o
747            except IOError as err:
748                raise error.general('writing output file: %s: %s' % (name, err))
749
750    def generate(self, name, tree = None, opts = None, macros = None):
751        self.buildset_start(name)
752        if tree is None:
753            tree = self.tree
754        if opts is None:
755            opts = self.opts
756        if macros is None:
757            macros = self.macros
758        bset = setbuilder.buildset(name, self.configs, opts, macros)
759        if name in tree:
760            raise error.general('duplicate build set in tree: %s' % (name))
761        tree[name] = { 'bset': { }, 'cfg': { 'file': []  } }
762        for c in bset.load():
763            macros = copy.copy(bset.macros)
764            if c.endswith('.bset'):
765                self.generate(c, tree[name]['bset'], bset.opts, macros)
766            elif c.endswith('.cfg'):
767                self.buildset_start(c)
768                self.config(config.file(c, bset.opts, macros),
769                            tree[name]['cfg'], bset.opts, macros)
770                self.buildset_end(c)
771            else:
772                raise error.general('invalid config type: %s' % (c))
773        self.buildset_end(name)
774
775    def create(self, inname, outname = None, intro_text = None):
776        self.introduction(inname, intro_text)
777        self.generate(inname)
778        if self.is_ini():
779            self.generate_ini()
780        self.epilogue(inname)
781        self.write(outname)
782
783def run(args):
784    try:
785        optargs = { '--list-bsets':   'List available build sets',
786                    '--list-configs': 'List available configurations',
787                    '--format':       'Output format (text, html, asciidoc, ini, xml)',
788                    '--output':       'File name to output the report' }
789        opts = options.load(args, optargs)
790        if opts.get_arg('--output') and len(opts.params()) > 1:
791            raise error.general('--output can only be used with a single config')
792        print('RTEMS Source Builder, Reporter, %s' % (version.str()))
793        opts.log_info()
794        if not check.host_setup(opts):
795            log.warning('forcing build with known host setup problems')
796        configs = build.get_configs(opts)
797        if not setbuilder.list_bset_cfg_files(opts, configs):
798            output = opts.get_arg('--output')
799            if output is not None:
800                output = output[1]
801            formatter = text_formatter()
802            format_opt = opts.get_arg('--format')
803            if format_opt:
804                if len(format_opt) != 2:
805                    raise error.general('invalid format option: %s' % ('='.join(format_opt)))
806                if format_opt[1] == 'text':
807                    pass
808                elif format_opt[1] == 'asciidoc':
809                    formatter = asciidoc_formatter()
810                elif format_opt[1] == 'html':
811                    formatter = html_formatter()
812                elif format_opt[1] == 'ini':
813                    formatter = ini_formatter()
814                elif format_opt[1] == 'xml':
815                    formatter = xml_formatter()
816                else:
817                    raise error.general('invalid format: %s' % (format_opt[1]))
818            r = report(formatter, configs, opts)
819            for _config in opts.params():
820                if output is None:
821                    outname = path.splitext(_config)[0] + formatter.ext()
822                    outname = outname.replace('/', '-')
823                else:
824                    outname = output
825                config = build.find_config(_config, configs)
826                if config is None:
827                    raise error.general('config file not found: %s' % (inname))
828                r.create(config, outname)
829            del r
830        else:
831            raise error.general('invalid config type: %s' % (config))
832    except error.general as gerr:
833        print(gerr)
834        sys.exit(1)
835    except error.internal as ierr:
836        print(ierr)
837        sys.exit(1)
838    except error.exit as eerr:
839        pass
840    except KeyboardInterrupt:
841        log.notice('abort: user terminated')
842        sys.exit(1)
843    sys.exit(0)
844
845if __name__ == "__main__":
846    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.