Changeset 5d1edd5 in rtems-tools for tester/rt/check.py


Ignore:
Timestamp:
Apr 24, 2017, 4:39:56 PM (2 years ago)
Author:
Chris Johns <chrisj@…>
Branches:
master
Children:
b89a7e4b
Parents:
224fb21
Message:

rtems-bsp-builder: Refactor for better config format, warnings and errors.

Refactor the code to improve the warnings and errors reporting.

Improve the configuration file format to better support any type
of build by separating the flags away from the builds.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • tester/rt/check.py

    r224fb21 r5d1edd5  
    11#
    22# RTEMS Tools Project (http://www.rtems.org/)
    3 # Copyright 2016 Chris Johns (chrisj@rtems.org)
     3# Copyright 2016-2017 Chris Johns (chrisj@rtems.org)
    44# All rights reserved.
    55#
     
    3535import operator
    3636import os
     37import re
    3738import sys
     39import textwrap
     40
     41import pprint
    3842
    3943try:
     
    4448from rtemstoolkit import execute
    4549from rtemstoolkit import error
     50from rtemstoolkit import host
    4651from rtemstoolkit import log
    4752from rtemstoolkit import path
     53from rtemstoolkit import textbox
    4854from rtemstoolkit import version
    4955
     
    5157    return version.version()
    5258
    53 class warnings_counter:
     59def wrap(line, lineend = '', indent = 0):
     60    if type(line) is tuple or type(line) is list:
     61        if len(line) >= 2:
     62            s1 = line[0]
     63        else:
     64            s1 = ''
     65        s2 = line[1:]
     66    elif type(line) is str:
     67        s1 = ''
     68        s2 = [line]
     69    else:
     70        raise error.internal('line is not a tuple, list or string')
     71    s = ''
     72    first = True
     73    for ss in s2:
     74        if type(ss) is not str and type(ss) is not unicode:
     75            raise error.internal('text needs to be a string')
     76        for l in textwrap.wrap(ss):
     77            s += '%s%s%s%s%s' % (' ' * indent, s1, l, lineend, os.linesep)
     78            if first and len(s1) > 0:
     79                s1 = ' ' * len(s1)
     80    if lineend != '':
     81        s = s[:0 - len(os.linesep) - 1] + os.linesep
     82    return s
     83
     84def title():
     85    return 'RTEMS Tools Project - RTEMS Kernel BSP Builder, %s' % (version.str())
     86
     87def command_line():
     88    return wrap(('command: ', ' '.join(sys.argv)), lineend = '\\')
     89
     90class warnings_errors:
    5491
    5592    def __init__(self, rtems):
    5693        self.rtems = path.host(rtems)
    5794        self.reset()
     95        self.groups = { 'groups'  : ['Shared', 'BSP', 'Network', 'Tests',
     96                                     'LibCPU', 'CPU Kit'],
     97                        'exclude' : '.*Makefile.*',
     98                        'CPU Kit' : '.*cpukit/.*',
     99                        'Network' : '.*libnetworking/.*',
     100                        'Tests'   : '.*testsuites/.*',
     101                        'BSP'     : '.*libbsp/.*',
     102                        'LibCPU'  : '.*libcpu/.*',
     103                        'Shared'  : '.*shared/.*' }
     104        self.arch = None
     105        self.bsp = None
     106        self.build = None
     107
     108    def _opts(self, arch = None, bsp = None, build = None):
     109        if arch is None:
     110            arch = self.arch
     111        if bsp is None:
     112            bsp = self.bsp
     113        if build is None:
     114            build = self.build
     115        return arch, bsp, build
     116
     117    def _key(self, arch, bsp, build):
     118        arch, bsp, build = self._opts(arch, bsp, build)
     119        return '%s/%s-%s' % (arch, bsp, build)
     120
     121    def _get_warnings(self, arch = None, bsp = None, build = None):
     122        arch, bsp, build = self._opts(arch = arch, bsp = bsp, build = build)
     123        if arch is None:
     124            arch = '.*'
     125        if bsp is None:
     126            bsp = '.*'
     127        if build is None:
     128            build = '.*'
     129        selector = re.compile('^%s/%s-%s$' % (arch, bsp, build))
     130        warnings = [w for w in self.warnings if selector.match(w)]
     131        return sorted(warnings)
     132
     133    def _total(self, archive):
     134        total = 0
     135        for a in archive:
     136            total += archive[a]
     137        return total
     138
     139    def _analyze(self, warnings, exclude):
     140        def _group(data, category, name, warning, count, groups, group_regx):
     141            if 'groups' not in data:
     142                data['groups'] = { }
     143            if category not in data['groups']:
     144                data['groups'][category] = { 'totals' : { } }
     145            if name not in data['groups'][category]:
     146                data['groups'][category][name] = { }
     147            for group in groups:
     148                if group not in data['groups'][category]['totals']:
     149                    data['groups'][category]['totals'][group] = 0
     150                if group not in data['groups'][category][name]:
     151                    data['groups'][category][name][group] = 0
     152                if group_regx[group].match(warning):
     153                    data['groups'][category][name][group] += count
     154                    data['groups'][category]['totals'][group] += count
     155                    break
     156
     157        def _update(data, category, name, warning, count, groups, group_regx):
     158            if category not in data:
     159                data[category] = { }
     160            if name not in data[category]:
     161                data[category][name] = { }
     162            if warning not in data[category][name]:
     163                data[category][name][warning] = 0
     164            data[category][name][warning] += count
     165            _group(data, category, name,  w, count, groups, group_regx)
     166
     167        data = { }
     168        group_regx = { }
     169        for group in self.groups['groups']:
     170            group_regx[group] = re.compile(self.groups[group])
     171        exclude_regx = re.compile(exclude)
     172        for warning in warnings:
     173            arch = warning.split('/', 1)[0]
     174            arch_bsp = warning.split('-', 1)[0]
     175            build = warning.split('-', 1)[1]
     176            for w in self.warnings[warning]:
     177                if not exclude_regx.match(w):
     178                    count = self.warnings[warning][w]
     179                    _update(data, 'arch',     arch,     w, count,
     180                           self.groups['groups'], group_regx)
     181                    _update(data, 'arch_bsp', arch_bsp, w, count,
     182                           self.groups['groups'], group_regx)
     183                    _update(data, 'build',  build,  w, count,
     184                           self.groups['groups'], group_regx)
     185        for category in ['arch', 'arch_bsp', 'build']:
     186            common = {}
     187            for name in data[category]:
     188                for w in data[category][name]:
     189                    if w not in common:
     190                        for other in [n for n in data[category] if n != name]:
     191                            if w in data[category][other]:
     192                                common[w] = data[category][name][w]
     193                                _group(data, category, 'common', w, common[w],
     194                                       self.groups['groups'], group_regx)
     195            data[category]['common'] = common
     196        return data
     197
     198    def _report_category(self, label, warnings, group_counts):
     199        width = 70
     200        cols_1 = [width]
     201        cols_2 = [8, width - 8]
     202        cols_4 = textbox.even_columns(4, width)
     203        cols_2_4 = textbox.merge_columns([cols_2, cols_4])
     204        s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     205        s += textbox.row(cols_1, [' ' + label], indent = 1)
     206        s += textbox.line(cols_1, marker = '+', indent = 1)
     207        builds = ['common'] + sorted([b for b in warnings if b != 'common'])
     208        common = warnings['common']
     209        for build in builds:
     210            build_warnings = warnings[build]
     211            if build is not 'common':
     212                build_warnings = [w for w in build_warnings if w not in common]
     213            s += textbox.row(cols_1,
     214                             [' %s : %d warning(s)' % (build,
     215                                                       len(build_warnings))],
     216                             indent = 1)
     217            if len(build_warnings) == 0:
     218                s += textbox.line(cols_1, marker = '+', indent = 1)
     219            else:
     220                s += textbox.line(cols_4, marker = '+', indent = 1)
     221                if build not in group_counts:
     222                    gs = [0 for group in self.groups['groups']]
     223                else:
     224                    gs = []
     225                    for g in range(0, len(self.groups['groups'])):
     226                        group = self.groups['groups'][g]
     227                        gs += ['%*s' % (cols_4[g % 4] - 2,
     228                                        '%s : %4d' % \
     229                                        (group,
     230                                         group_counts[build][group]))]
     231                    for row in range(0, len(self.groups['groups']), 4):
     232                        if row + 4 > len(self.groups['groups']):
     233                            d = gs[row:] + \
     234                                ['' for r in range(row,
     235                                                   len(self.groups['groups']))]
     236                        else:
     237                            d = gs[row:+4]
     238                        s += textbox.row(cols_4, d, indent = 1)
     239                s += textbox.line(cols_2_4, marker = '+', indent = 1)
     240                vw = sorted([(w, warnings[build][w]) for w in build_warnings],
     241                            key = operator.itemgetter(1),
     242                            reverse = True)
     243                for w in vw:
     244                    c1 = '%6d' % w[1]
     245                    for l in textwrap.wrap(' ' + w[0]):
     246                        s += textbox.row(cols_2, [c1, l], indent = 1)
     247                        c1 = ' ' * 6
     248                s += textbox.line(cols_2, marker = '+', indent = 1)
     249        return s
     250
     251    def _report_warning_map(self):
     252        builds = self.messages['warnings']
     253        width = 70
     254        cols_1 = [width]
     255        s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     256        s += textbox.row(cols_1, [' Warning Map'], indent = 1)
     257        s += textbox.line(cols_1, marker = '+', indent = 1)
     258        for build in builds:
     259            messages = builds[build]
     260            s += textbox.row(cols_1, [' %s : %d' % (build, len(messages))], indent = 1)
     261            s += textbox.line(cols_1, marker = '+', indent = 1)
     262            for msg in messages:
     263                for l in textwrap.wrap(msg, width = width - 3):
     264                    s += textbox.row(cols_1, [' ' + l], indent = 1)
     265                for l in textwrap.wrap(messages[msg], width = width - 3 - 4):
     266                    s += textbox.row(cols_1, ['    ' + l], indent = 1)
     267            s += textbox.line(cols_1, marker = '+', indent = 1)
     268        return s
    58269
    59270    def report(self):
    60         str = ''
    61         sw = sorted(self.warnings.items(), key = operator.itemgetter(1), reverse = True)
    62         for w in sw:
    63             str += ' %5d %s%s' % (w[1], w[0], os.linesep)
    64         return str
    65 
    66     def accumulate(self, total):
    67         for w in self.warnings:
    68             if w not in total.warnings:
    69                 total.warnings[w] = self.warnings[w]
    70             else:
    71                 total.warnings[w] += self.warnings[w]
    72         total.count += self.count
    73 
    74     def get(self):
    75         return self.count
     271        arch, bsp, build = self._opts()
     272        warnings = self._get_warnings(arch, bsp, build)
     273        total = 0
     274        for build in warnings:
     275            total += self._total(self.warnings[build])
     276        if total == 0:
     277            s = ' No warnings'
     278        else:
     279            data = self._analyze(warnings, self.groups['exclude'])
     280            s = self._report_category('By Architecture (total : %d)' % (total),
     281                                      data['arch'], data['groups']['arch'])
     282            s += os.linesep
     283            s += self._report_category('By BSP (total : %d)' % (total),
     284                                       data['arch_bsp'], data['groups']['arch_bsp'])
     285            s += os.linesep
     286            s += self._report_category('By Build (total : %d)' % (total),
     287                                       data['build'], data['groups']['build'])
     288            s += os.linesep
     289            s += self._report_warning_map()
     290            s += os.linesep
     291
     292        return s
     293
     294    def set_build(self, arch, bsp, build):
     295        self.arch = arch
     296        self.bsp = bsp
     297        self.build = build
     298        self.build_key = '%s/%s-%s' % (arch, bsp, build)
     299        if self.build_key not in self.warnings:
     300            self.warnings[self.build_key] = {}
     301        if self.build_key not in self.errors:
     302            self.errors[self.build_key] = {}
     303
     304    def clear_build(self):
     305        self.arch = None
     306        self.bsp = None
     307        self.build = None
     308        self.build_key = None
     309
     310    def get_warning_count(self):
     311        return self.warning_count
     312
     313    def get_error_count(self):
     314        return self.error_count
    76315
    77316    def reset(self):
    78317        self.warnings = { }
    79         self.count = 0
     318        self.warning_count = 0
     319        self.errors = { }
     320        self.error_count = 0
     321        self.messages = { 'warnings' : { }, 'errors' : { } }
     322
     323    def get_warning_messages(self, arch = None, bsp = None, build = None):
     324        messages = self.messages['warnings'][self._key(arch, bsp, build)]
     325        return ['%s %s' % (m, messages[m]) for m in messages]
     326
     327    def get_error_messages(self, arch = None, bsp = None, build = None):
     328        messages = self.messages['errors'][self._key(arch, bsp, build)]
     329        return ['%s %s' % (m, messages[m]) for m in messages]
    80330
    81331    def output(self, text):
    82         for l in text.splitlines():
    83             if ' warning:' in l:
    84                 self.count += 1
    85                 ws = l.split(' ')
    86                 if len(ws) > 0:
    87                     ws = ws[0].split(':')
    88                     w = path.abspath(ws[0])
    89                     w = w.replace(self.rtems, '')
    90                     if path.isabspath(w):
    91                         w = w[1:]
    92                     #
    93                     # Ignore compiler option warnings.
    94                     #
    95                     if len(ws) >= 3:
    96                         w = '%s:%s:%s' % (w, ws[1], ws[2])
    97                         if w not in self.warnings:
    98                             self.warnings[w] = 0
    99                         self.warnings[w] += 1
     332        def _line_split(line, source_base):
     333            ls = line.split(' ', 1)
     334            fname = ls[0].split(':')
     335            #
     336            # Ignore compiler option warnings.
     337            #
     338            if len(fname) < 4:
     339                return None
     340            p = path.abspath(fname[0])
     341            p = p.replace(source_base, '')
     342            if path.isabspath(p):
     343                p = p[1:]
     344            return p, fname[1], fname[2], ls[1]
     345
     346        if self.build_key is not None and \
     347           (' warning:' in text or ' error:' in text):
     348            for l in text.splitlines():
     349                if ' warning:' in l:
     350                    self.warning_count += 1
     351                    archive = self.warnings[self.build_key]
     352                    messages = 'warnings'
     353                elif ' error:' in l:
     354                    self.error_count += 1
     355                    archive = self.errors[self.build_key]
     356                    messages = 'errors'
     357                else:
     358                    continue
     359                line_parts = _line_split(l, self.rtems)
     360                if line_parts is not None:
     361                    src, line, pos, msg = line_parts
     362                    where = '%s:%s:%s' % (src, line, pos)
     363                    if where not in archive:
     364                        archive[where] = 1
     365                    else:
     366                        archive[where] += 1
     367                    if self.build_key not in self.messages[messages]:
     368                        self.messages[messages][self.build_key] = { }
     369                    self.messages[messages][self.build_key][where] = msg
     370
    100371        log.output(text)
    101372
     
    109380        return '%s/%s' % (arch, bsp)
    110381
    111     def add(self, good, arch, bsp, configure, warnings):
     382    def add(self, good, arch, bsp, configure, warnings, error_messages):
    112383        if good:
    113             self.passes += [(arch, bsp, configure, warnings)]
     384            self.passes += [(arch, bsp, configure, warnings, None)]
    114385        else:
    115             self.fails += [(arch, bsp, configure, 0)]
     386            self.fails += [(arch, bsp, configure, warnings, error_messages)]
    116387
    117388    def report(self):
     
    124395        log.output(' Failures:')
    125396        if len(self.fails) == 0:
    126             log.output('None')
     397            log.output('  None')
    127398        else:
    128399            max_col = 0
     
    136407                if config_at != -1:
    137408                    config_cmd = config_cmd[config_at:]
    138                 log.output(' %*s:  %s' % (max_col + 2,
    139                                           self._arch_bsp(f[0], f[1]),
    140                                           config_cmd))
     409                s1 = ' %*s:  ' % (max_col + 2, self._arch_bsp(f[0], f[1]))
     410                log.output(wrap([s1, config_cmd], lineend = '\\'))
     411                if f[4] is not None:
     412                    s1 = ' ' * len(s1)
     413                    for msg in f[4]:
     414                        log.output(wrap([s1, msg], lineend = '\\'))
    141415        log.output(' Passes:')
    142416        if len(self.passes) == 0:
    143             log.output('None')
     417            log.output('  None')
    144418        else:
    145419            max_col = 0
     
    153427                if config_at != -1:
    154428                    config_cmd = config_cmd[config_at:]
    155                 log.output(' %*s:  %5d  %s' % (max_col + 2,
    156                                                self._arch_bsp(f[0], f[1]),
    157                                                f[3],
    158                                                config_cmd))
     429                log.output(wrap((' %*s:  %5d  ' % (max_col + 2,
     430                                                   self._arch_bsp(f[0], f[1]),
     431                                                   f[3]),
     432                                 config_cmd),
     433                                lineend = '\\'))
    159434
    160435class configuration:
     
    164439        self.name = None
    165440        self.archs = { }
    166         self.builds = { }
     441        self.builds_ = { }
    167442        self.profiles = { }
     443        self.configurations = { }
    168444
    169445    def __str__(self):
     
    173449             pprint.pformat(self.archs, indent = 1, width = 80) + os.linesep
    174450        s += 'Builds:' + os.linesep + \
    175              pprint.pformat(self.builds, indent = 1, width = 80) + os.linesep
     451             pprint.pformat(self.builds_, indent = 1, width = 80) + os.linesep
    176452        s += 'Profiles:' + os.linesep + \
    177453             pprint.pformat(self.profiles, indent = 1, width = 80) + os.linesep
     
    184460        except:
    185461            if err:
    186                 raise error.general('config: no %s found in %s' % (label, section))
     462                raise error.general('config: no "%s" found in "%s"' % (label, section))
    187463        return None
    188464
    189465    def _get_items(self, section, err = True):
    190466        try:
    191             items = self.config.items(section)
     467            items = [(name, key.replace(os.linesep, ' ')) for name, key in self.config.items(section)]
    192468            return items
    193469        except:
    194470            if err:
    195                 raise error.general('config: section %s not found' % (section))
     471                raise error.general('config: section "%s" not found' % (section))
    196472        return []
    197473
     
    202478        return sorted(set([a.strip() for a in items.split(',')]))
    203479
    204     def load(self, name, variation):
     480    def _get_item_names(self, section, err = True):
     481        try:
     482            return [item[0] for item in self.config.items(section)]
     483        except:
     484            if err:
     485                raise error.general('config: section "%s" not found' % (section))
     486        return []
     487
     488    def _build_options(self, build, nesting = 0):
     489        if ':' in build:
     490            section, name = build.split(':', 1)
     491            opts = [self._get_item(section, name)]
     492            return opts
     493        builds = self.builds_['builds']
     494        if build not in builds:
     495            raise error.general('build %s not found' % (build))
     496        if nesting > 20:
     497            raise error.general('nesting build %s' % (build))
     498        options = []
     499        for option in self.builds_['builds'][build]:
     500            if ':' in option:
     501                section, name = option.split(':', 1)
     502                opts = [self._get_item(section, name)]
     503            else:
     504                opts = self._options(option, nesting + 1)
     505            for opt in opts:
     506                if opt not in options:
     507                    options += [opt]
     508        return options
     509
     510    def load(self, name, build):
    205511        if not path.exists(name):
    206512            raise error.general('config: cannot read configuration: %s' % (name))
     
    240546            self.archs[a] = arch
    241547        builds = {}
    242         builds['default'] = self._get_item('builds', 'default').split()
    243         builds['variations'] = self._comma_list('builds', 'variations')
    244         if variation is None:
    245             variation = builds['default']
    246         builds['variation'] = variation
    247         builds['base'] = self._get_item('builds', 'standard').split()
    248         builds['variations'] = self._comma_list('builds', variation)
    249         builds['var_options'] = {}
    250         for v in builds['variations']:
    251             if v == 'base':
    252                 builds['var_options'][v] = self._get_item('builds', v).split()
    253             else:
    254                 builds['var_options'][v] = []
    255         self.builds = builds
    256 
    257     def variations(self):
    258         return self.builds['variations']
     548        builds['default'] = self._get_item('builds', 'default')
     549        if build is None:
     550            build = builds['default']
     551        builds['config'] = { }
     552        for config in self._get_items('config'):
     553            builds['config'][config[0]] = config[1]
     554        builds['build'] = build
     555        builds_ = self._get_item_names('builds')
     556        builds['builds'] = {}
     557        for build in builds_:
     558            build_builds = self._comma_list('builds', build)
     559            has_config = False
     560            has_build = False
     561            for b in build_builds:
     562                if ':' in b:
     563                    if has_build:
     564                        raise error.general('config and build in build: %s' % (build))
     565                    has_config = True
     566                else:
     567                    if has_config:
     568                        raise error.general('config and build in build: %s' % (build))
     569                    has_build = True
     570            builds['builds'][build] = build_builds
     571        self.builds_ = builds
     572
     573    def build(self):
     574        return self.builds_['build']
     575
     576    def builds(self):
     577        if self.builds_['build'] in self.builds_['builds']:
     578            build = self.builds_['builds'][self.builds_['build']]
     579            if ':' in build[0]:
     580                return [self.builds_['build']]
     581            return build
     582        return None
     583
     584    def build_options(self, build):
     585        return ' '.join(self._build_options(build))
    259586
    260587    def excludes(self, arch):
     
    287614        return self.archs[arch][bsp]['bspopts']
    288615
    289     def base(self):
    290         return self.builds['base']
    291 
    292     def variant_options(self, variant):
    293         if variant in self.builds['var_options']:
    294             return self.builds['var_options'][variant]
    295         return []
    296 
    297616    def profile_present(self, profile):
    298617        return profile in self.profiles
     
    303622    def profile_arch_bsps(self, profile, arch):
    304623        return self.profiles[profile]['bsps_%s' % (arch)]
     624
     625    def report(self, profiles = True, builds = True, architectures = True):
     626        width = 70
     627        cols_1 = [width]
     628        cols_2 = [10, width - 10]
     629        s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     630        s1 = ' File'
     631        colon = ':'
     632        for l in textwrap.wrap(self.name, width = cols_2[1] - 3):
     633            s += textbox.row(cols_2, [s1, ' ' + l], marker = colon, indent = 1)
     634            colon = ' '
     635            s1 = ' ' * len(s1)
     636        s += textbox.line(cols_1, marker = '+', indent = 1)
     637        s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     638        if profiles:
     639            profiles = sorted(self.profiles['profiles'])
     640            bsps = 0
     641            for profile in profiles:
     642                archs = sorted(self.profiles[profile]['archs'])
     643                for arch in archs:
     644                    bsps += len(self.profiles[profile]['bsps_%s' % (arch)])
     645            s += textbox.row(cols_1,
     646                             [' Profiles : %d/%d' % (len(archs), bsps)],
     647                             indent = 1)
     648            for profile in profiles:
     649                textbox.row(cols_2,
     650                            [profile, self.profiles[profile]['name']],
     651                            indent = 1)
     652            s += textbox.line(cols_1, marker = '+', indent = 1)
     653            for profile in profiles:
     654                s += textbox.row(cols_1, [' %s' % (profile)], indent = 1)
     655                profile = self.profiles[profile]
     656                archs = sorted(profile['archs'])
     657                for arch in archs:
     658                    s += textbox.line(cols_2, marker = '+', indent = 1)
     659                    s1 = ' ' + arch
     660                    for l in textwrap.wrap(', '.join(profile['bsps_%s' % (arch)]),
     661                                           width = cols_2[1] - 2):
     662                        s += textbox.row(cols_2, [s1, ' ' + l], indent = 1)
     663                        s1 = ' ' * len(s1)
     664                s += textbox.line(cols_2, marker = '+', indent = 1)
     665        if builds:
     666            s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     667            s += textbox.row(cols_1,
     668                             [' Builds:  %s (default)' % (self.builds_['default'])],
     669                             indent = 1)
     670            builds = self.builds_['builds']
     671            bsize = 0
     672            for build in builds:
     673                if len(build) > bsize:
     674                    bsize = len(build)
     675            cols_b = [bsize + 2, width - bsize - 2]
     676            s += textbox.line(cols_b, marker = '+', indent = 1)
     677            for build in builds:
     678                s1 = ' ' + build
     679                for l in textwrap.wrap(', '.join(builds[build]),
     680                                       width = cols_b[1] - 2):
     681                    s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
     682                    s1 = ' ' * len(s1)
     683                s += textbox.line(cols_b, marker = '+', indent = 1)
     684            configs = self.builds_['config']
     685            s += textbox.row(cols_1,
     686                             [' Configure Options: %d' % (len(configs))],
     687                             indent = 1)
     688            csize = 0
     689            for config in configs:
     690                if len(config) > csize:
     691                    csize = len(config)
     692            cols_c = [csize + 3, width - csize - 3]
     693            s += textbox.line(cols_c, marker = '+', indent = 1)
     694            for config in configs:
     695                s1 = ' ' + config
     696                for l in textwrap.wrap(configs[config], width = cols_c[1] - 3):
     697                    s += textbox.row(cols_c, [s1, ' ' + l], indent = 1)
     698                    s1 = ' ' * len(s1)
     699                s += textbox.line(cols_c, marker = '+', indent = 1)
     700        if architectures:
     701            s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
     702            archs = sorted(self.archs.keys())
     703            bsps = 0
     704            asize = 0
     705            for arch in archs:
     706                if len(arch) > asize:
     707                    asize = len(arch)
     708                bsps += len(self.archs[arch]['bsps'])
     709            s += textbox.row(cols_1,
     710                             [' Architectures : %d (bsps: %d)' % (len(archs), bsps)],
     711                             indent = 1)
     712            cols_a = [asize + 2, width - asize - 2]
     713            s += textbox.line(cols_a, marker = '+', indent = 1)
     714            for arch in archs:
     715                s += textbox.row(cols_a,
     716                                 [' ' + arch, ' %d' % (len(self.archs[arch]['bsps']))],
     717                                 indent = 1)
     718            s += textbox.line(cols_a, marker = '+', indent = 1)
     719            for archn in archs:
     720                arch = self.archs[archn]
     721                if len(arch['bsps']) > 0:
     722                    bsize = 0
     723                    for bsp in arch['bsps']:
     724                        if len(bsp) > bsize:
     725                            bsize = len(bsp)
     726                    cols_b = [bsize + 3, width - bsize - 3]
     727                    s += textbox.row(cols_1, [' ' + archn + ':'], indent = 1)
     728                    s += textbox.line(cols_b, marker = '+', indent = 1)
     729                    for bsp in arch['bsps']:
     730                        s1 = ' ' + bsp
     731                        bspopts = ' '.join(arch[bsp]['bspopts'])
     732                        if len(bspopts):
     733                            for l in textwrap.wrap('bopt: ' + bspopts,
     734                                                   width = cols_b[1] - 3):
     735                                s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
     736                                s1 = ' ' * len(s1)
     737                        excludes = []
     738                        for exclude in arch['excludes']:
     739                            if bsp in arch['excludes'][exclude]:
     740                                excludes += [exclude]
     741                        excludes = ', '.join(excludes)
     742                        if len(excludes):
     743                            for l in textwrap.wrap('ex: ' + excludes,
     744                                                   width = cols_b[1] - 3):
     745                                s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
     746                                s1 = ' ' * len(s1)
     747                        if len(bspopts) == 0 and len(excludes) == 0:
     748                            s += textbox.row(cols_b, [s1, ' '], indent = 1)
     749                    s += textbox.line(cols_b, marker = '+', indent = 1)
     750        return s
    305751
    306752class build:
     
    316762        self.errors = { 'configure': 0,
    317763                        'build':     0,
    318                         'tests':     0 }
     764                        'tests':     0,
     765                        'fails':     []}
    319766        self.counts = { 'h'        : 0,
    320767                        'exes'     : 0,
    321768                        'objs'     : 0,
    322769                        'libs'     : 0 }
    323         self.warnings = warnings_counter(rtems)
     770        self.warnings_errors = warnings_errors(rtems)
    324771        self.results = results()
    325772        if not path.exists(path.join(rtems, 'configure')) or \
     
    341788        return self.config.arch_bsps(arch)
    342789
    343     def _variations(self, arch, bsp):
    344         def _match(var, vars):
    345             matches = []
    346             for v in vars:
    347                 if var in v.split('-'):
    348                     matches += [v]
    349             return matches
    350 
    351         vars = self.config.variations()
    352         for v in self.config.excludes(arch):
    353             for m in _match(v, vars):
    354                 vars.remove(m)
    355         for v in self.config.bsp_excludes(arch, bsp):
    356             for m in _match(v, vars):
    357                 vars.remove(m)
    358         return vars
     790    def _build(self):
     791        return self.config.build()
     792
     793    def _builds(self, arch, bsp):
     794        builds = self.config.builds()
     795        if builds is None:
     796            return None
     797        for b in self.config.excludes(arch):
     798            builds.remove(b)
     799        for b in self.config.bsp_excludes(arch, bsp):
     800            builds.remove(b)
     801        return builds
    359802
    360803    def _arch_bsp_dir_make(self, arch, bsp):
     
    367810
    368811    def _config_command(self, commands, arch, bsp):
     812        if type(commands) is not list:
     813            commands = [commands]
    369814        cmd = [path.join(self.rtems, 'configure')]
    370815        commands += self.config.bspopts(arch, bsp)
     
    377822        return ' '.join(cmd)
    378823
    379     def _build_set(self, variations):
     824    def _build_set(self, builds):
    380825        build_set = { }
    381         bs = self.config.base()
    382         for var in variations:
    383             build_set[var] = bs + self.config.variant_options(var)
     826        for build in builds:
     827            build_set[build] = self.config.build_options(build)
    384828        return build_set
    385829
     
    407851        return counts
    408852
     853    def _have_failures(self, fails):
     854        return len(fails) != 0
     855
     856    def _warnings_report(self):
     857        if self.options['warnings-report'] is not None:
     858            with open(self.options['warnings-report'], 'w') as f:
     859                f.write(title() + os.linesep)
     860                f.write(os.linesep)
     861                f.write('Date: %s%s' % (datetime.date.today().strftime('%c'), os.linesep))
     862                f.write(os.linesep)
     863                f.write(command_line() + os.linesep)
     864                f.write(self.warnings_errors.report())
     865
     866    def _finished(self):
     867        log.notice('+  warnings:%d  exes:%d  objs:%d  libs:%d' % \
     868                   (self.warnings_errors.get_warning_count(), self.counts['exes'],
     869                    self.counts['objs'], self.counts['libs']))
     870        log.output()
     871        log.output('Warnings:')
     872        log.output(self.warnings_errors.report())
     873        log.output()
     874        log.notice('Failures:')
     875        log.notice(self.failures_report(self.errors['fails']))
     876        self._warnings_report()
     877
     878    def failures_report(self, fails):
     879        if not self._have_failures(fails):
     880            return ' No failure(s)'
     881        absize = 0
     882        bsize = 0
     883        ssize = 0
     884        for f in fails:
     885            arch_bsp = '%s/%s' % (f[1], f[2])
     886            if len(arch_bsp) > absize:
     887                absize = len(arch_bsp)
     888            if len(f[3]) > bsize:
     889                bsize = len(f[3])
     890            if len(f[0]) > ssize:
     891                ssize = len(f[0])
     892        fc = 1
     893        s = ''
     894        for f in fails:
     895            fcl = '%3d' % (fc)
     896            arch_bsp = '%s/%s' % (f[1], f[2])
     897            state = f[0]
     898            s1 = '%s %-*s %-*s %-*s ' % (fcl, bsize, f[3], absize, arch_bsp, ssize, state)
     899            s += wrap((s1, f[4]), lineend = '\\')
     900            s1 = ' ' * (len(s1) + 2)
     901            for e in self.warnings_errors.get_error_messages(f[1], f[2], f[3]):
     902                s += wrap([s1, 'error: ' + e])
     903            fc += 1
     904        return s
     905
    409906    def build_arch_bsp(self, arch, bsp):
    410907        if not self.config.bsp_present(arch, bsp):
     
    415912        self._arch_bsp_dir_clean(arch, bsp)
    416913        self._arch_bsp_dir_make(arch, bsp)
    417         variations = self._variations(arch, bsp)
    418         build_set = self._build_set(variations)
     914        builds = self._builds(arch, bsp)
     915        if builds is None:
     916            raise error.general('build not found: %s' % (self._build()))
     917        build_set = self._build_set(builds)
    419918        bsp_start = datetime.datetime.now()
    420         bsp_warnings = warnings_counter(self.rtems)
    421919        env_path = os.environ['PATH']
    422920        os.environ['PATH'] = path.host(path.join(self.tools, 'bin')) + \
    423921                             os.pathsep + os.environ['PATH']
     922        fails = []
    424923        for bs in sorted(build_set.keys()):
    425             warnings = warnings_counter(self.rtems)
     924            self.warnings_errors.set_build(arch, bsp, bs)
    426925            start = datetime.datetime.now()
    427926            log.output('- ' * 35)
    428927            log.notice('. Configuring: %s' % (bs))
    429928            try:
     929                warnings = self.warnings_errors.get_warning_count()
    430930                result = '+ Pass'
    431931                bpath = self._build_dir(arch, bsp, bs)
     932                good = True
     933                error_messages = None
    432934                path.mkdir(bpath)
    433935                config_cmd = self._config_command(build_set[bs], arch, bsp)
    434936                cmd = config_cmd
    435                 e = execute.capture_execution(log = warnings)
    436                 log.output('run: ' + cmd)
     937                e = execute.capture_execution(log = self.warnings_errors)
     938                log.output(wrap(('run: ', cmd), lineend = '\\'))
    437939                if self.options['dry-run']:
    438940                    exit_code = 0
     
    441943                if exit_code != 0:
    442944                    result = '- FAIL'
     945                    failure = ('configure', arch, bsp, bs, config_cmd)
     946                    fails += [failure]
    443947                    self.errors['configure'] += 1
     948                    self.errors['fails'] += [failure]
    444949                    log.notice('- Configure failed: %s' % (bs))
    445950                    log.output('cmd failed: %s' % (cmd))
    446                     if self.options['stop-on-error']:
    447                         raise error.general('Configuring %s failed' % (bs))
     951                    good = False
    448952                else:
    449953                    log.notice('. Building: %s' % (bs))
     
    457961                        exit_code, proc, output = e.shell(cmd, cwd = path.host(bpath))
    458962                    if exit_code != 0:
     963                        error_messages = self.warnings_errors.get_error_messages()
    459964                        result = '- FAIL'
     965                        failure = ('build', arch, bsp, bs, config_cmd, error_messages)
     966                        fails += [failure]
    460967                        self.errors['build'] += 1
     968                        self.errors['fails'] += [failure]
    461969                        log.notice('- FAIL: %s: %s' % (bs, self._error_str()))
    462970                        log.output('cmd failed: %s' % (cmd))
    463                         if self.options['stop-on-error']:
    464                             raise error.general('Building %s failed' % (bs))
     971                        good = False
    465972                    files = self._count_files(arch, bsp, bs)
    466973                    log.notice('%s: %s: warnings:%d  exes:%d  objs:%s  libs:%d' % \
    467                                (result, bs, warnings.get(),
     974                               (result, bs,
     975                                self.warnings_errors.get_warning_count() - warnings,
    468976                                files['exes'], files['objs'], files['libs']))
    469977                log.notice('  %s' % (self._error_str()))
    470                 self.results.add(result[0] == '+', arch, bsp, config_cmd, warnings.get())
     978                self.results.add(good, arch, bsp, config_cmd,
     979                                 self.warnings_errors.get_warning_count() - warnings,
     980                                 error_messages)
     981                if not good and self.options['stop-on-error']:
     982                    raise error.general('Configuring %s failed' % (bs))
    471983            finally:
    472984                end = datetime.datetime.now()
     
    475987                    path.removeall(self._build_dir(arch, bsp, bs))
    476988            log.notice('^ Time %s' % (str(end - start)))
    477             log.output('Warnings Report:')
    478             log.output(warnings.report())
    479             warnings.accumulate(bsp_warnings)
    480             warnings.accumulate(self.warnings)
     989            self.warnings_errors.clear_build()
    481990        bsp_end = datetime.datetime.now()
    482991        log.notice('^ BSP Time %s' % (str(bsp_end - bsp_start)))
    483         log.output('BSP Warnings Report:')
    484         log.output(bsp_warnings.report())
     992        log.output('Failure Report:')
     993        log.output(self.failures_report(fails))
    485994        os.environ['PATH'] = env_path
     995
     996    def build_bsp(self, arch, bsp):
     997        self.build_arch_bsp(arch, bsp)
     998        self._finished()
    486999
    4871000    def build_arch(self, arch):
     
    4951008        end = datetime.datetime.now()
    4961009        log.notice('^ Architecture Time %s' % (str(end - start)))
    497         log.notice('  warnings:%d  exes:%d  objs:%d  libs:%d' % \
    498                    (self.warnings.get(), self.counts['exes'],
    499                     self.counts['objs'], self.counts['libs']))
    500         log.output('Architecture Warnings:')
    501         log.output(self.warnings.report())
     1010        self._finished()
    5021011
    5031012    def build(self):
     
    5051014            self.build_arch(arch)
    5061015        log.notice('^ Profile Time %s' % (str(end - start)))
    507         log.notice('+  warnings:%d  exes:%d  objs:%d  libs:%d' % \
    508                    (self.warnings.get(), self.counts['exes'],
    509                     self.counts['objs'], self.counts['libs']))
    510         log.output('Profile Warnings:')
    511         log.output(self.warnings.report())
     1016        self._finished()
    5121017
    5131018    def build_profile(self, profile):
     
    5211026        end = datetime.datetime.now()
    5221027        log.notice('^ Profile Time %s' % (str(end - start)))
    523         log.notice('  warnings:%d  exes:%d  objs:%d  libs:%d' % \
    524                    (self.warnings.get(), self.counts['exes'],
    525                     self.counts['objs'], self.counts['libs']))
    526         log.output('Profile Warnings:')
    527         log.output(self.warnings.report())
     1028        self._finished()
    5281029
    5291030def run_args(args):
     
    5461047        tools = prefix
    5471048        build_dir = 'bsp-builds'
    548         logf = 'bsp-build-%s.txt' % (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
    549         config_file = path.join(top, 'share', 'rtems', 'tester', 'rtems', 'rtems-bsps.ini')
     1049        logf = 'bsp-build-%s.txt' % \
     1050               (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
     1051        config_file = path.join(top, 'share', 'rtems', 'tester',
     1052                                'rtems', 'rtems-bsps.ini')
    5501053        if not path.exists(config_file):
    5511054            config_file = path.join(top, 'tester', 'rtems', 'rtems-bsps.ini')
    5521055
    5531056        argsp = argparse.ArgumentParser()
    554         argsp.add_argument('--prefix', help = 'Prefix to build the BSP.', type = str)
    555         argsp.add_argument('--rtems-tools', help = 'The RTEMS tools directory.', type = str)
    556         argsp.add_argument('--rtems', help = 'The RTEMS source tree.', type = str)
    557         argsp.add_argument('--build-path', help = 'Path to build in.', type = str)
     1057        argsp.add_argument('--prefix', help = 'Prefix to build the BSP.',
     1058                           type = str)
     1059        argsp.add_argument('--rtems-tools', help = 'The RTEMS tools directory.',
     1060                           type = str)
     1061        argsp.add_argument('--rtems', help = 'The RTEMS source tree.',
     1062                           type = str)
     1063        argsp.add_argument('--config-report', help = 'Report the configuration.',
     1064                           action = 'store_true')
     1065        argsp.add_argument('--warnings-report', help = 'Report the warnings to a file.',
     1066                           type = str, default = None)
     1067        argsp.add_argument('--build-path', help = 'Path to build in.',
     1068                           type = str)
    5581069        argsp.add_argument('--log', help = 'Log file.', type = str)
    5591070        argsp.add_argument('--stop-on-error', help = 'Stop on an error.',
     
    5631074        argsp.add_argument('--profiles', help = 'Build the listed profiles.',
    5641075                           type = str, default = 'tier-1')
    565         argsp.add_argument('--build', help = 'Build variation.', type = str, default='all')
    566         argsp.add_argument('--arch', help = 'Build the specific architecture.', type = str)
    567         argsp.add_argument('--bsp', help = 'Build the specific BSP.', type = str)
     1076        argsp.add_argument('--build', help = 'Build name to build.',
     1077                           type = str, default='all')
     1078        argsp.add_argument('--arch', help = 'Build the specific architecture.',
     1079                           type = str)
     1080        argsp.add_argument('--bsp', help = 'Build the specific BSP.',
     1081                           type = str)
     1082        argsp.add_argument('--jobs', help = 'Number of jobs to run.',
     1083                           type = int, default = host.cpus())
    5681084        argsp.add_argument('--dry-run', help = 'Do not run the actual builds.',
    5691085                           action = 'store_true')
     
    5731089            logf = opts.log
    5741090        log.default = log.log([logf])
    575         log.notice('RTEMS Tools Project - RTEMS Kernel BSP Builder, %s' % (version.str()))
     1091        log.notice(title())
     1092        log.output(command_line())
    5761093        if opts.rtems is None:
    5771094            raise error.general('No RTEMS source provided on the command line')
     
    5881105        config.load(config_file, opts.build)
    5891106
    590         options = { 'stop-on-error' : opts.stop_on_error,
    591                     'no-clean'      : opts.no_clean,
    592                     'dry-run'       : opts.dry_run,
    593                     'jobs'          : 8 }
     1107        if opts.config_report:
     1108            log.notice('Configuration Report:')
     1109            log.notice(config.report())
     1110            sys.exit(0)
     1111
     1112        options = { 'stop-on-error'   : opts.stop_on_error,
     1113                    'no-clean'        : opts.no_clean,
     1114                    'dry-run'         : opts.dry_run,
     1115                    'jobs'            : opts.jobs,
     1116                    'warnings-report' : opts.warnings_report }
    5941117
    5951118        b = build(config, rtems_version(), prefix, tools,
     
    5971120        if opts.arch is not None:
    5981121            if opts.bsp is not None:
    599                 b.build_arch_bsp(opts.arch, opts.bsp)
     1122                b.build_bsp(opts.arch, opts.bsp)
    6001123            else:
    6011124                b.build_arch(opts.arch)
Note: See TracChangeset for help on using the changeset viewer.