source: rtems-source-builder/source-builder/sb/reports.py @ 158ad68

4.11
Last change on this file since 158ad68 was 158ad68, checked in by Chris Johns <chrisj@…>, on 10/03/20 at 11:53:04

sb: Back port the RTEMS 5 and 6 RSB engine.

  • Build GDb first as we do for RTEMS 5 and later
  • Update GDB to 9.1 for all archs expect SPARC. The SIS patches only apply to 7.9. Disable Python for SPARC

Closes #4111

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