source: rtems-source-builder/source-builder/sb/reports.py @ 910196d

5
Last change on this file since 910196d was 910196d, checked in by Chris Johns <chrisj@…>, on 10/27/17 at 06:24:50

sb: Add a call to get the post processed report output.

Update #3210.

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