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

4.11
Last change on this file since e8f5111 was e8f5111, checked in by Chris Johns <chrisj@…>, on Dec 3, 2015 at 11:22:17 AM

sb. Add VERSION support for releasing the RSB.

Add support to release the RSB by adding the VERSION file. The file
is a single line with the version.

Fix the reports to include the version. Update the INI file
support to include the details of the build.

Show the GIT or released version when the command starts.

Closes #2480.

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