summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2017-04-25 02:39:56 +1000
committerChris Johns <chrisj@rtems.org>2017-04-25 02:39:56 +1000
commit5d1edd55c84c20afcb72ae3eb5369b80bedc3071 (patch)
treeda627f35292170965d48d255ec8488b3889396be
parentrtemstoolkit: Add a textbox module for formatted reporting. (diff)
downloadrtems-tools-5d1edd55c84c20afcb72ae3eb5369b80bedc3071.tar.bz2
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.
-rwxr-xr-xtester/rt/check.py819
-rw-r--r--tester/rtems/rtems-bsps.ini71
2 files changed, 716 insertions, 174 deletions
diff --git a/tester/rt/check.py b/tester/rt/check.py
index 7e1fc3f..d33d606 100755
--- a/tester/rt/check.py
+++ b/tester/rt/check.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2016 Chris Johns (chrisj@rtems.org)
+# Copyright 2016-2017 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -34,7 +34,11 @@ import argparse
import datetime
import operator
import os
+import re
import sys
+import textwrap
+
+import pprint
try:
import configparser
@@ -43,60 +47,327 @@ except:
from rtemstoolkit import execute
from rtemstoolkit import error
+from rtemstoolkit import host
from rtemstoolkit import log
from rtemstoolkit import path
+from rtemstoolkit import textbox
from rtemstoolkit import version
def rtems_version():
return version.version()
-class warnings_counter:
+def wrap(line, lineend = '', indent = 0):
+ if type(line) is tuple or type(line) is list:
+ if len(line) >= 2:
+ s1 = line[0]
+ else:
+ s1 = ''
+ s2 = line[1:]
+ elif type(line) is str:
+ s1 = ''
+ s2 = [line]
+ else:
+ raise error.internal('line is not a tuple, list or string')
+ s = ''
+ first = True
+ for ss in s2:
+ if type(ss) is not str and type(ss) is not unicode:
+ raise error.internal('text needs to be a string')
+ for l in textwrap.wrap(ss):
+ s += '%s%s%s%s%s' % (' ' * indent, s1, l, lineend, os.linesep)
+ if first and len(s1) > 0:
+ s1 = ' ' * len(s1)
+ if lineend != '':
+ s = s[:0 - len(os.linesep) - 1] + os.linesep
+ return s
+
+def title():
+ return 'RTEMS Tools Project - RTEMS Kernel BSP Builder, %s' % (version.str())
+
+def command_line():
+ return wrap(('command: ', ' '.join(sys.argv)), lineend = '\\')
+
+class warnings_errors:
def __init__(self, rtems):
self.rtems = path.host(rtems)
self.reset()
+ self.groups = { 'groups' : ['Shared', 'BSP', 'Network', 'Tests',
+ 'LibCPU', 'CPU Kit'],
+ 'exclude' : '.*Makefile.*',
+ 'CPU Kit' : '.*cpukit/.*',
+ 'Network' : '.*libnetworking/.*',
+ 'Tests' : '.*testsuites/.*',
+ 'BSP' : '.*libbsp/.*',
+ 'LibCPU' : '.*libcpu/.*',
+ 'Shared' : '.*shared/.*' }
+ self.arch = None
+ self.bsp = None
+ self.build = None
+
+ def _opts(self, arch = None, bsp = None, build = None):
+ if arch is None:
+ arch = self.arch
+ if bsp is None:
+ bsp = self.bsp
+ if build is None:
+ build = self.build
+ return arch, bsp, build
+
+ def _key(self, arch, bsp, build):
+ arch, bsp, build = self._opts(arch, bsp, build)
+ return '%s/%s-%s' % (arch, bsp, build)
+
+ def _get_warnings(self, arch = None, bsp = None, build = None):
+ arch, bsp, build = self._opts(arch = arch, bsp = bsp, build = build)
+ if arch is None:
+ arch = '.*'
+ if bsp is None:
+ bsp = '.*'
+ if build is None:
+ build = '.*'
+ selector = re.compile('^%s/%s-%s$' % (arch, bsp, build))
+ warnings = [w for w in self.warnings if selector.match(w)]
+ return sorted(warnings)
+
+ def _total(self, archive):
+ total = 0
+ for a in archive:
+ total += archive[a]
+ return total
+
+ def _analyze(self, warnings, exclude):
+ def _group(data, category, name, warning, count, groups, group_regx):
+ if 'groups' not in data:
+ data['groups'] = { }
+ if category not in data['groups']:
+ data['groups'][category] = { 'totals' : { } }
+ if name not in data['groups'][category]:
+ data['groups'][category][name] = { }
+ for group in groups:
+ if group not in data['groups'][category]['totals']:
+ data['groups'][category]['totals'][group] = 0
+ if group not in data['groups'][category][name]:
+ data['groups'][category][name][group] = 0
+ if group_regx[group].match(warning):
+ data['groups'][category][name][group] += count
+ data['groups'][category]['totals'][group] += count
+ break
+
+ def _update(data, category, name, warning, count, groups, group_regx):
+ if category not in data:
+ data[category] = { }
+ if name not in data[category]:
+ data[category][name] = { }
+ if warning not in data[category][name]:
+ data[category][name][warning] = 0
+ data[category][name][warning] += count
+ _group(data, category, name, w, count, groups, group_regx)
+
+ data = { }
+ group_regx = { }
+ for group in self.groups['groups']:
+ group_regx[group] = re.compile(self.groups[group])
+ exclude_regx = re.compile(exclude)
+ for warning in warnings:
+ arch = warning.split('/', 1)[0]
+ arch_bsp = warning.split('-', 1)[0]
+ build = warning.split('-', 1)[1]
+ for w in self.warnings[warning]:
+ if not exclude_regx.match(w):
+ count = self.warnings[warning][w]
+ _update(data, 'arch', arch, w, count,
+ self.groups['groups'], group_regx)
+ _update(data, 'arch_bsp', arch_bsp, w, count,
+ self.groups['groups'], group_regx)
+ _update(data, 'build', build, w, count,
+ self.groups['groups'], group_regx)
+ for category in ['arch', 'arch_bsp', 'build']:
+ common = {}
+ for name in data[category]:
+ for w in data[category][name]:
+ if w not in common:
+ for other in [n for n in data[category] if n != name]:
+ if w in data[category][other]:
+ common[w] = data[category][name][w]
+ _group(data, category, 'common', w, common[w],
+ self.groups['groups'], group_regx)
+ data[category]['common'] = common
+ return data
+
+ def _report_category(self, label, warnings, group_counts):
+ width = 70
+ cols_1 = [width]
+ cols_2 = [8, width - 8]
+ cols_4 = textbox.even_columns(4, width)
+ cols_2_4 = textbox.merge_columns([cols_2, cols_4])
+ s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ s += textbox.row(cols_1, [' ' + label], indent = 1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ builds = ['common'] + sorted([b for b in warnings if b != 'common'])
+ common = warnings['common']
+ for build in builds:
+ build_warnings = warnings[build]
+ if build is not 'common':
+ build_warnings = [w for w in build_warnings if w not in common]
+ s += textbox.row(cols_1,
+ [' %s : %d warning(s)' % (build,
+ len(build_warnings))],
+ indent = 1)
+ if len(build_warnings) == 0:
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ else:
+ s += textbox.line(cols_4, marker = '+', indent = 1)
+ if build not in group_counts:
+ gs = [0 for group in self.groups['groups']]
+ else:
+ gs = []
+ for g in range(0, len(self.groups['groups'])):
+ group = self.groups['groups'][g]
+ gs += ['%*s' % (cols_4[g % 4] - 2,
+ '%s : %4d' % \
+ (group,
+ group_counts[build][group]))]
+ for row in range(0, len(self.groups['groups']), 4):
+ if row + 4 > len(self.groups['groups']):
+ d = gs[row:] + \
+ ['' for r in range(row,
+ len(self.groups['groups']))]
+ else:
+ d = gs[row:+4]
+ s += textbox.row(cols_4, d, indent = 1)
+ s += textbox.line(cols_2_4, marker = '+', indent = 1)
+ vw = sorted([(w, warnings[build][w]) for w in build_warnings],
+ key = operator.itemgetter(1),
+ reverse = True)
+ for w in vw:
+ c1 = '%6d' % w[1]
+ for l in textwrap.wrap(' ' + w[0]):
+ s += textbox.row(cols_2, [c1, l], indent = 1)
+ c1 = ' ' * 6
+ s += textbox.line(cols_2, marker = '+', indent = 1)
+ return s
+
+ def _report_warning_map(self):
+ builds = self.messages['warnings']
+ width = 70
+ cols_1 = [width]
+ s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ s += textbox.row(cols_1, [' Warning Map'], indent = 1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ for build in builds:
+ messages = builds[build]
+ s += textbox.row(cols_1, [' %s : %d' % (build, len(messages))], indent = 1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ for msg in messages:
+ for l in textwrap.wrap(msg, width = width - 3):
+ s += textbox.row(cols_1, [' ' + l], indent = 1)
+ for l in textwrap.wrap(messages[msg], width = width - 3 - 4):
+ s += textbox.row(cols_1, [' ' + l], indent = 1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ return s
def report(self):
- str = ''
- sw = sorted(self.warnings.items(), key = operator.itemgetter(1), reverse = True)
- for w in sw:
- str += ' %5d %s%s' % (w[1], w[0], os.linesep)
- return str
-
- def accumulate(self, total):
- for w in self.warnings:
- if w not in total.warnings:
- total.warnings[w] = self.warnings[w]
- else:
- total.warnings[w] += self.warnings[w]
- total.count += self.count
+ arch, bsp, build = self._opts()
+ warnings = self._get_warnings(arch, bsp, build)
+ total = 0
+ for build in warnings:
+ total += self._total(self.warnings[build])
+ if total == 0:
+ s = ' No warnings'
+ else:
+ data = self._analyze(warnings, self.groups['exclude'])
+ s = self._report_category('By Architecture (total : %d)' % (total),
+ data['arch'], data['groups']['arch'])
+ s += os.linesep
+ s += self._report_category('By BSP (total : %d)' % (total),
+ data['arch_bsp'], data['groups']['arch_bsp'])
+ s += os.linesep
+ s += self._report_category('By Build (total : %d)' % (total),
+ data['build'], data['groups']['build'])
+ s += os.linesep
+ s += self._report_warning_map()
+ s += os.linesep
+
+ return s
+
+ def set_build(self, arch, bsp, build):
+ self.arch = arch
+ self.bsp = bsp
+ self.build = build
+ self.build_key = '%s/%s-%s' % (arch, bsp, build)
+ if self.build_key not in self.warnings:
+ self.warnings[self.build_key] = {}
+ if self.build_key not in self.errors:
+ self.errors[self.build_key] = {}
+
+ def clear_build(self):
+ self.arch = None
+ self.bsp = None
+ self.build = None
+ self.build_key = None
- def get(self):
- return self.count
+ def get_warning_count(self):
+ return self.warning_count
+
+ def get_error_count(self):
+ return self.error_count
def reset(self):
self.warnings = { }
- self.count = 0
+ self.warning_count = 0
+ self.errors = { }
+ self.error_count = 0
+ self.messages = { 'warnings' : { }, 'errors' : { } }
+
+ def get_warning_messages(self, arch = None, bsp = None, build = None):
+ messages = self.messages['warnings'][self._key(arch, bsp, build)]
+ return ['%s %s' % (m, messages[m]) for m in messages]
+
+ def get_error_messages(self, arch = None, bsp = None, build = None):
+ messages = self.messages['errors'][self._key(arch, bsp, build)]
+ return ['%s %s' % (m, messages[m]) for m in messages]
def output(self, text):
- for l in text.splitlines():
- if ' warning:' in l:
- self.count += 1
- ws = l.split(' ')
- if len(ws) > 0:
- ws = ws[0].split(':')
- w = path.abspath(ws[0])
- w = w.replace(self.rtems, '')
- if path.isabspath(w):
- w = w[1:]
- #
- # Ignore compiler option warnings.
- #
- if len(ws) >= 3:
- w = '%s:%s:%s' % (w, ws[1], ws[2])
- if w not in self.warnings:
- self.warnings[w] = 0
- self.warnings[w] += 1
+ def _line_split(line, source_base):
+ ls = line.split(' ', 1)
+ fname = ls[0].split(':')
+ #
+ # Ignore compiler option warnings.
+ #
+ if len(fname) < 4:
+ return None
+ p = path.abspath(fname[0])
+ p = p.replace(source_base, '')
+ if path.isabspath(p):
+ p = p[1:]
+ return p, fname[1], fname[2], ls[1]
+
+ if self.build_key is not None and \
+ (' warning:' in text or ' error:' in text):
+ for l in text.splitlines():
+ if ' warning:' in l:
+ self.warning_count += 1
+ archive = self.warnings[self.build_key]
+ messages = 'warnings'
+ elif ' error:' in l:
+ self.error_count += 1
+ archive = self.errors[self.build_key]
+ messages = 'errors'
+ else:
+ continue
+ line_parts = _line_split(l, self.rtems)
+ if line_parts is not None:
+ src, line, pos, msg = line_parts
+ where = '%s:%s:%s' % (src, line, pos)
+ if where not in archive:
+ archive[where] = 1
+ else:
+ archive[where] += 1
+ if self.build_key not in self.messages[messages]:
+ self.messages[messages][self.build_key] = { }
+ self.messages[messages][self.build_key][where] = msg
+
log.output(text)
class results:
@@ -108,11 +379,11 @@ class results:
def _arch_bsp(self, arch, bsp):
return '%s/%s' % (arch, bsp)
- def add(self, good, arch, bsp, configure, warnings):
+ def add(self, good, arch, bsp, configure, warnings, error_messages):
if good:
- self.passes += [(arch, bsp, configure, warnings)]
+ self.passes += [(arch, bsp, configure, warnings, None)]
else:
- self.fails += [(arch, bsp, configure, 0)]
+ self.fails += [(arch, bsp, configure, warnings, error_messages)]
def report(self):
log.notice('* Passes: %d Failures: %d' %
@@ -123,7 +394,7 @@ class results:
(len(self.passes), len(self.fails)))
log.output(' Failures:')
if len(self.fails) == 0:
- log.output('None')
+ log.output(' None')
else:
max_col = 0
for f in self.fails:
@@ -135,12 +406,15 @@ class results:
config_at = config_cmd.find('configure')
if config_at != -1:
config_cmd = config_cmd[config_at:]
- log.output(' %*s: %s' % (max_col + 2,
- self._arch_bsp(f[0], f[1]),
- config_cmd))
+ s1 = ' %*s: ' % (max_col + 2, self._arch_bsp(f[0], f[1]))
+ log.output(wrap([s1, config_cmd], lineend = '\\'))
+ if f[4] is not None:
+ s1 = ' ' * len(s1)
+ for msg in f[4]:
+ log.output(wrap([s1, msg], lineend = '\\'))
log.output(' Passes:')
if len(self.passes) == 0:
- log.output('None')
+ log.output(' None')
else:
max_col = 0
for f in self.passes:
@@ -152,10 +426,11 @@ class results:
config_at = config_cmd.find('configure')
if config_at != -1:
config_cmd = config_cmd[config_at:]
- log.output(' %*s: %5d %s' % (max_col + 2,
- self._arch_bsp(f[0], f[1]),
- f[3],
- config_cmd))
+ log.output(wrap((' %*s: %5d ' % (max_col + 2,
+ self._arch_bsp(f[0], f[1]),
+ f[3]),
+ config_cmd),
+ lineend = '\\'))
class configuration:
@@ -163,8 +438,9 @@ class configuration:
self.config = configparser.ConfigParser()
self.name = None
self.archs = { }
- self.builds = { }
+ self.builds_ = { }
self.profiles = { }
+ self.configurations = { }
def __str__(self):
import pprint
@@ -172,7 +448,7 @@ class configuration:
s += 'Archs:' + os.linesep + \
pprint.pformat(self.archs, indent = 1, width = 80) + os.linesep
s += 'Builds:' + os.linesep + \
- pprint.pformat(self.builds, indent = 1, width = 80) + os.linesep
+ pprint.pformat(self.builds_, indent = 1, width = 80) + os.linesep
s += 'Profiles:' + os.linesep + \
pprint.pformat(self.profiles, indent = 1, width = 80) + os.linesep
return s
@@ -183,16 +459,16 @@ class configuration:
return rec
except:
if err:
- raise error.general('config: no %s found in %s' % (label, section))
+ raise error.general('config: no "%s" found in "%s"' % (label, section))
return None
def _get_items(self, section, err = True):
try:
- items = self.config.items(section)
+ items = [(name, key.replace(os.linesep, ' ')) for name, key in self.config.items(section)]
return items
except:
if err:
- raise error.general('config: section %s not found' % (section))
+ raise error.general('config: section "%s" not found' % (section))
return []
def _comma_list(self, section, label, error = True):
@@ -201,7 +477,37 @@ class configuration:
return []
return sorted(set([a.strip() for a in items.split(',')]))
- def load(self, name, variation):
+ def _get_item_names(self, section, err = True):
+ try:
+ return [item[0] for item in self.config.items(section)]
+ except:
+ if err:
+ raise error.general('config: section "%s" not found' % (section))
+ return []
+
+ def _build_options(self, build, nesting = 0):
+ if ':' in build:
+ section, name = build.split(':', 1)
+ opts = [self._get_item(section, name)]
+ return opts
+ builds = self.builds_['builds']
+ if build not in builds:
+ raise error.general('build %s not found' % (build))
+ if nesting > 20:
+ raise error.general('nesting build %s' % (build))
+ options = []
+ for option in self.builds_['builds'][build]:
+ if ':' in option:
+ section, name = option.split(':', 1)
+ opts = [self._get_item(section, name)]
+ else:
+ opts = self._options(option, nesting + 1)
+ for opt in opts:
+ if opt not in options:
+ options += [opt]
+ return options
+
+ def load(self, name, build):
if not path.exists(name):
raise error.general('config: cannot read configuration: %s' % (name))
self.name = name
@@ -239,23 +545,44 @@ class configuration:
arch[b]['bspopts'] = self._comma_list(a, 'bspopts_%s' % (b), error = False)
self.archs[a] = arch
builds = {}
- builds['default'] = self._get_item('builds', 'default').split()
- builds['variations'] = self._comma_list('builds', 'variations')
- if variation is None:
- variation = builds['default']
- builds['variation'] = variation
- builds['base'] = self._get_item('builds', 'standard').split()
- builds['variations'] = self._comma_list('builds', variation)
- builds['var_options'] = {}
- for v in builds['variations']:
- if v == 'base':
- builds['var_options'][v] = self._get_item('builds', v).split()
- else:
- builds['var_options'][v] = []
- self.builds = builds
+ builds['default'] = self._get_item('builds', 'default')
+ if build is None:
+ build = builds['default']
+ builds['config'] = { }
+ for config in self._get_items('config'):
+ builds['config'][config[0]] = config[1]
+ builds['build'] = build
+ builds_ = self._get_item_names('builds')
+ builds['builds'] = {}
+ for build in builds_:
+ build_builds = self._comma_list('builds', build)
+ has_config = False
+ has_build = False
+ for b in build_builds:
+ if ':' in b:
+ if has_build:
+ raise error.general('config and build in build: %s' % (build))
+ has_config = True
+ else:
+ if has_config:
+ raise error.general('config and build in build: %s' % (build))
+ has_build = True
+ builds['builds'][build] = build_builds
+ self.builds_ = builds
- def variations(self):
- return self.builds['variations']
+ def build(self):
+ return self.builds_['build']
+
+ def builds(self):
+ if self.builds_['build'] in self.builds_['builds']:
+ build = self.builds_['builds'][self.builds_['build']]
+ if ':' in build[0]:
+ return [self.builds_['build']]
+ return build
+ return None
+
+ def build_options(self, build):
+ return ' '.join(self._build_options(build))
def excludes(self, arch):
excludes = self.archs[arch]['excludes'].keys()
@@ -286,14 +613,6 @@ class configuration:
def bspopts(self, arch, bsp):
return self.archs[arch][bsp]['bspopts']
- def base(self):
- return self.builds['base']
-
- def variant_options(self, variant):
- if variant in self.builds['var_options']:
- return self.builds['var_options'][variant]
- return []
-
def profile_present(self, profile):
return profile in self.profiles
@@ -303,6 +622,133 @@ class configuration:
def profile_arch_bsps(self, profile, arch):
return self.profiles[profile]['bsps_%s' % (arch)]
+ def report(self, profiles = True, builds = True, architectures = True):
+ width = 70
+ cols_1 = [width]
+ cols_2 = [10, width - 10]
+ s = textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ s1 = ' File'
+ colon = ':'
+ for l in textwrap.wrap(self.name, width = cols_2[1] - 3):
+ s += textbox.row(cols_2, [s1, ' ' + l], marker = colon, indent = 1)
+ colon = ' '
+ s1 = ' ' * len(s1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ if profiles:
+ profiles = sorted(self.profiles['profiles'])
+ bsps = 0
+ for profile in profiles:
+ archs = sorted(self.profiles[profile]['archs'])
+ for arch in archs:
+ bsps += len(self.profiles[profile]['bsps_%s' % (arch)])
+ s += textbox.row(cols_1,
+ [' Profiles : %d/%d' % (len(archs), bsps)],
+ indent = 1)
+ for profile in profiles:
+ textbox.row(cols_2,
+ [profile, self.profiles[profile]['name']],
+ indent = 1)
+ s += textbox.line(cols_1, marker = '+', indent = 1)
+ for profile in profiles:
+ s += textbox.row(cols_1, [' %s' % (profile)], indent = 1)
+ profile = self.profiles[profile]
+ archs = sorted(profile['archs'])
+ for arch in archs:
+ s += textbox.line(cols_2, marker = '+', indent = 1)
+ s1 = ' ' + arch
+ for l in textwrap.wrap(', '.join(profile['bsps_%s' % (arch)]),
+ width = cols_2[1] - 2):
+ s += textbox.row(cols_2, [s1, ' ' + l], indent = 1)
+ s1 = ' ' * len(s1)
+ s += textbox.line(cols_2, marker = '+', indent = 1)
+ if builds:
+ s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ s += textbox.row(cols_1,
+ [' Builds: %s (default)' % (self.builds_['default'])],
+ indent = 1)
+ builds = self.builds_['builds']
+ bsize = 0
+ for build in builds:
+ if len(build) > bsize:
+ bsize = len(build)
+ cols_b = [bsize + 2, width - bsize - 2]
+ s += textbox.line(cols_b, marker = '+', indent = 1)
+ for build in builds:
+ s1 = ' ' + build
+ for l in textwrap.wrap(', '.join(builds[build]),
+ width = cols_b[1] - 2):
+ s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
+ s1 = ' ' * len(s1)
+ s += textbox.line(cols_b, marker = '+', indent = 1)
+ configs = self.builds_['config']
+ s += textbox.row(cols_1,
+ [' Configure Options: %d' % (len(configs))],
+ indent = 1)
+ csize = 0
+ for config in configs:
+ if len(config) > csize:
+ csize = len(config)
+ cols_c = [csize + 3, width - csize - 3]
+ s += textbox.line(cols_c, marker = '+', indent = 1)
+ for config in configs:
+ s1 = ' ' + config
+ for l in textwrap.wrap(configs[config], width = cols_c[1] - 3):
+ s += textbox.row(cols_c, [s1, ' ' + l], indent = 1)
+ s1 = ' ' * len(s1)
+ s += textbox.line(cols_c, marker = '+', indent = 1)
+ if architectures:
+ s += textbox.line(cols_1, line = '=', marker = '+', indent = 1)
+ archs = sorted(self.archs.keys())
+ bsps = 0
+ asize = 0
+ for arch in archs:
+ if len(arch) > asize:
+ asize = len(arch)
+ bsps += len(self.archs[arch]['bsps'])
+ s += textbox.row(cols_1,
+ [' Architectures : %d (bsps: %d)' % (len(archs), bsps)],
+ indent = 1)
+ cols_a = [asize + 2, width - asize - 2]
+ s += textbox.line(cols_a, marker = '+', indent = 1)
+ for arch in archs:
+ s += textbox.row(cols_a,
+ [' ' + arch, ' %d' % (len(self.archs[arch]['bsps']))],
+ indent = 1)
+ s += textbox.line(cols_a, marker = '+', indent = 1)
+ for archn in archs:
+ arch = self.archs[archn]
+ if len(arch['bsps']) > 0:
+ bsize = 0
+ for bsp in arch['bsps']:
+ if len(bsp) > bsize:
+ bsize = len(bsp)
+ cols_b = [bsize + 3, width - bsize - 3]
+ s += textbox.row(cols_1, [' ' + archn + ':'], indent = 1)
+ s += textbox.line(cols_b, marker = '+', indent = 1)
+ for bsp in arch['bsps']:
+ s1 = ' ' + bsp
+ bspopts = ' '.join(arch[bsp]['bspopts'])
+ if len(bspopts):
+ for l in textwrap.wrap('bopt: ' + bspopts,
+ width = cols_b[1] - 3):
+ s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
+ s1 = ' ' * len(s1)
+ excludes = []
+ for exclude in arch['excludes']:
+ if bsp in arch['excludes'][exclude]:
+ excludes += [exclude]
+ excludes = ', '.join(excludes)
+ if len(excludes):
+ for l in textwrap.wrap('ex: ' + excludes,
+ width = cols_b[1] - 3):
+ s += textbox.row(cols_b, [s1, ' ' + l], indent = 1)
+ s1 = ' ' * len(s1)
+ if len(bspopts) == 0 and len(excludes) == 0:
+ s += textbox.row(cols_b, [s1, ' '], indent = 1)
+ s += textbox.line(cols_b, marker = '+', indent = 1)
+ return s
+
class build:
def __init__(self, config, version, prefix, tools, rtems, build_dir, options):
@@ -315,12 +761,13 @@ class build:
self.options = options
self.errors = { 'configure': 0,
'build': 0,
- 'tests': 0 }
+ 'tests': 0,
+ 'fails': []}
self.counts = { 'h' : 0,
'exes' : 0,
'objs' : 0,
'libs' : 0 }
- self.warnings = warnings_counter(rtems)
+ self.warnings_errors = warnings_errors(rtems)
self.results = results()
if not path.exists(path.join(rtems, 'configure')) or \
not path.exists(path.join(rtems, 'Makefile.in')) or \
@@ -340,22 +787,18 @@ class build:
def _bsps(self, arch):
return self.config.arch_bsps(arch)
- def _variations(self, arch, bsp):
- def _match(var, vars):
- matches = []
- for v in vars:
- if var in v.split('-'):
- matches += [v]
- return matches
-
- vars = self.config.variations()
- for v in self.config.excludes(arch):
- for m in _match(v, vars):
- vars.remove(m)
- for v in self.config.bsp_excludes(arch, bsp):
- for m in _match(v, vars):
- vars.remove(m)
- return vars
+ def _build(self):
+ return self.config.build()
+
+ def _builds(self, arch, bsp):
+ builds = self.config.builds()
+ if builds is None:
+ return None
+ for b in self.config.excludes(arch):
+ builds.remove(b)
+ for b in self.config.bsp_excludes(arch, bsp):
+ builds.remove(b)
+ return builds
def _arch_bsp_dir_make(self, arch, bsp):
if not path.exists(self._path(arch, bsp)):
@@ -366,6 +809,8 @@ class build:
path.removeall(self._path(arch, bsp))
def _config_command(self, commands, arch, bsp):
+ if type(commands) is not list:
+ commands = [commands]
cmd = [path.join(self.rtems, 'configure')]
commands += self.config.bspopts(arch, bsp)
for c in commands:
@@ -376,11 +821,10 @@ class build:
cmd += [c]
return ' '.join(cmd)
- def _build_set(self, variations):
+ def _build_set(self, builds):
build_set = { }
- bs = self.config.base()
- for var in variations:
- build_set[var] = bs + self.config.variant_options(var)
+ for build in builds:
+ build_set[build] = self.config.build_options(build)
return build_set
def _build_dir(self, arch, bsp, build):
@@ -406,6 +850,59 @@ class build:
self.counts[f] += counts[f]
return counts
+ def _have_failures(self, fails):
+ return len(fails) != 0
+
+ def _warnings_report(self):
+ if self.options['warnings-report'] is not None:
+ with open(self.options['warnings-report'], 'w') as f:
+ f.write(title() + os.linesep)
+ f.write(os.linesep)
+ f.write('Date: %s%s' % (datetime.date.today().strftime('%c'), os.linesep))
+ f.write(os.linesep)
+ f.write(command_line() + os.linesep)
+ f.write(self.warnings_errors.report())
+
+ def _finished(self):
+ log.notice('+ warnings:%d exes:%d objs:%d libs:%d' % \
+ (self.warnings_errors.get_warning_count(), self.counts['exes'],
+ self.counts['objs'], self.counts['libs']))
+ log.output()
+ log.output('Warnings:')
+ log.output(self.warnings_errors.report())
+ log.output()
+ log.notice('Failures:')
+ log.notice(self.failures_report(self.errors['fails']))
+ self._warnings_report()
+
+ def failures_report(self, fails):
+ if not self._have_failures(fails):
+ return ' No failure(s)'
+ absize = 0
+ bsize = 0
+ ssize = 0
+ for f in fails:
+ arch_bsp = '%s/%s' % (f[1], f[2])
+ if len(arch_bsp) > absize:
+ absize = len(arch_bsp)
+ if len(f[3]) > bsize:
+ bsize = len(f[3])
+ if len(f[0]) > ssize:
+ ssize = len(f[0])
+ fc = 1
+ s = ''
+ for f in fails:
+ fcl = '%3d' % (fc)
+ arch_bsp = '%s/%s' % (f[1], f[2])
+ state = f[0]
+ s1 = '%s %-*s %-*s %-*s ' % (fcl, bsize, f[3], absize, arch_bsp, ssize, state)
+ s += wrap((s1, f[4]), lineend = '\\')
+ s1 = ' ' * (len(s1) + 2)
+ for e in self.warnings_errors.get_error_messages(f[1], f[2], f[3]):
+ s += wrap([s1, 'error: ' + e])
+ fc += 1
+ return s
+
def build_arch_bsp(self, arch, bsp):
if not self.config.bsp_present(arch, bsp):
raise error.general('BSP not found: %s/%s' % (arch, bsp))
@@ -414,37 +911,44 @@ class build:
log.notice('. Creating: %s' % (self._path(arch, bsp)))
self._arch_bsp_dir_clean(arch, bsp)
self._arch_bsp_dir_make(arch, bsp)
- variations = self._variations(arch, bsp)
- build_set = self._build_set(variations)
+ builds = self._builds(arch, bsp)
+ if builds is None:
+ raise error.general('build not found: %s' % (self._build()))
+ build_set = self._build_set(builds)
bsp_start = datetime.datetime.now()
- bsp_warnings = warnings_counter(self.rtems)
env_path = os.environ['PATH']
os.environ['PATH'] = path.host(path.join(self.tools, 'bin')) + \
os.pathsep + os.environ['PATH']
+ fails = []
for bs in sorted(build_set.keys()):
- warnings = warnings_counter(self.rtems)
+ self.warnings_errors.set_build(arch, bsp, bs)
start = datetime.datetime.now()
log.output('- ' * 35)
log.notice('. Configuring: %s' % (bs))
try:
+ warnings = self.warnings_errors.get_warning_count()
result = '+ Pass'
bpath = self._build_dir(arch, bsp, bs)
+ good = True
+ error_messages = None
path.mkdir(bpath)
config_cmd = self._config_command(build_set[bs], arch, bsp)
cmd = config_cmd
- e = execute.capture_execution(log = warnings)
- log.output('run: ' + cmd)
+ e = execute.capture_execution(log = self.warnings_errors)
+ log.output(wrap(('run: ', cmd), lineend = '\\'))
if self.options['dry-run']:
exit_code = 0
else:
exit_code, proc, output = e.shell(cmd, cwd = path.host(bpath))
if exit_code != 0:
result = '- FAIL'
+ failure = ('configure', arch, bsp, bs, config_cmd)
+ fails += [failure]
self.errors['configure'] += 1
+ self.errors['fails'] += [failure]
log.notice('- Configure failed: %s' % (bs))
log.output('cmd failed: %s' % (cmd))
- if self.options['stop-on-error']:
- raise error.general('Configuring %s failed' % (bs))
+ good = False
else:
log.notice('. Building: %s' % (bs))
cmd = 'make'
@@ -456,34 +960,43 @@ class build:
else:
exit_code, proc, output = e.shell(cmd, cwd = path.host(bpath))
if exit_code != 0:
+ error_messages = self.warnings_errors.get_error_messages()
result = '- FAIL'
+ failure = ('build', arch, bsp, bs, config_cmd, error_messages)
+ fails += [failure]
self.errors['build'] += 1
+ self.errors['fails'] += [failure]
log.notice('- FAIL: %s: %s' % (bs, self._error_str()))
log.output('cmd failed: %s' % (cmd))
- if self.options['stop-on-error']:
- raise error.general('Building %s failed' % (bs))
+ good = False
files = self._count_files(arch, bsp, bs)
log.notice('%s: %s: warnings:%d exes:%d objs:%s libs:%d' % \
- (result, bs, warnings.get(),
+ (result, bs,
+ self.warnings_errors.get_warning_count() - warnings,
files['exes'], files['objs'], files['libs']))
log.notice(' %s' % (self._error_str()))
- self.results.add(result[0] == '+', arch, bsp, config_cmd, warnings.get())
+ self.results.add(good, arch, bsp, config_cmd,
+ self.warnings_errors.get_warning_count() - warnings,
+ error_messages)
+ if not good and self.options['stop-on-error']:
+ raise error.general('Configuring %s failed' % (bs))
finally:
end = datetime.datetime.now()
if not self.options['no-clean']:
log.notice('. Cleaning: %s' % (self._build_dir(arch, bsp, bs)))
path.removeall(self._build_dir(arch, bsp, bs))
log.notice('^ Time %s' % (str(end - start)))
- log.output('Warnings Report:')
- log.output(warnings.report())
- warnings.accumulate(bsp_warnings)
- warnings.accumulate(self.warnings)
+ self.warnings_errors.clear_build()
bsp_end = datetime.datetime.now()
log.notice('^ BSP Time %s' % (str(bsp_end - bsp_start)))
- log.output('BSP Warnings Report:')
- log.output(bsp_warnings.report())
+ log.output('Failure Report:')
+ log.output(self.failures_report(fails))
os.environ['PATH'] = env_path
+ def build_bsp(self, arch, bsp):
+ self.build_arch_bsp(arch, bsp)
+ self._finished()
+
def build_arch(self, arch):
start = datetime.datetime.now()
log.output('=' * 70)
@@ -494,21 +1007,13 @@ class build:
self.build_arch_bsp(arch, bsp)
end = datetime.datetime.now()
log.notice('^ Architecture Time %s' % (str(end - start)))
- log.notice(' warnings:%d exes:%d objs:%d libs:%d' % \
- (self.warnings.get(), self.counts['exes'],
- self.counts['objs'], self.counts['libs']))
- log.output('Architecture Warnings:')
- log.output(self.warnings.report())
+ self._finished()
def build(self):
for arch in self.config.archs():
self.build_arch(arch)
log.notice('^ Profile Time %s' % (str(end - start)))
- log.notice('+ warnings:%d exes:%d objs:%d libs:%d' % \
- (self.warnings.get(), self.counts['exes'],
- self.counts['objs'], self.counts['libs']))
- log.output('Profile Warnings:')
- log.output(self.warnings.report())
+ self._finished()
def build_profile(self, profile):
if not self.config.profile_present(profile):
@@ -520,11 +1025,7 @@ class build:
self.build_arch_bsp(arch, bsp)
end = datetime.datetime.now()
log.notice('^ Profile Time %s' % (str(end - start)))
- log.notice(' warnings:%d exes:%d objs:%d libs:%d' % \
- (self.warnings.get(), self.counts['exes'],
- self.counts['objs'], self.counts['libs']))
- log.output('Profile Warnings:')
- log.output(self.warnings.report())
+ self._finished()
def run_args(args):
b = None
@@ -545,16 +1046,26 @@ def run_args(args):
prefix = '/opt/rtems/%s' % (rtems_version())
tools = prefix
build_dir = 'bsp-builds'
- logf = 'bsp-build-%s.txt' % (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
- config_file = path.join(top, 'share', 'rtems', 'tester', 'rtems', 'rtems-bsps.ini')
+ logf = 'bsp-build-%s.txt' % \
+ (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
+ config_file = path.join(top, 'share', 'rtems', 'tester',
+ 'rtems', 'rtems-bsps.ini')
if not path.exists(config_file):
config_file = path.join(top, 'tester', 'rtems', 'rtems-bsps.ini')
argsp = argparse.ArgumentParser()
- argsp.add_argument('--prefix', help = 'Prefix to build the BSP.', type = str)
- argsp.add_argument('--rtems-tools', help = 'The RTEMS tools directory.', type = str)
- argsp.add_argument('--rtems', help = 'The RTEMS source tree.', type = str)
- argsp.add_argument('--build-path', help = 'Path to build in.', type = str)
+ argsp.add_argument('--prefix', help = 'Prefix to build the BSP.',
+ type = str)
+ argsp.add_argument('--rtems-tools', help = 'The RTEMS tools directory.',
+ type = str)
+ argsp.add_argument('--rtems', help = 'The RTEMS source tree.',
+ type = str)
+ argsp.add_argument('--config-report', help = 'Report the configuration.',
+ action = 'store_true')
+ argsp.add_argument('--warnings-report', help = 'Report the warnings to a file.',
+ type = str, default = None)
+ argsp.add_argument('--build-path', help = 'Path to build in.',
+ type = str)
argsp.add_argument('--log', help = 'Log file.', type = str)
argsp.add_argument('--stop-on-error', help = 'Stop on an error.',
action = 'store_true')
@@ -562,9 +1073,14 @@ def run_args(args):
action = 'store_true')
argsp.add_argument('--profiles', help = 'Build the listed profiles.',
type = str, default = 'tier-1')
- argsp.add_argument('--build', help = 'Build variation.', type = str, default='all')
- argsp.add_argument('--arch', help = 'Build the specific architecture.', type = str)
- argsp.add_argument('--bsp', help = 'Build the specific BSP.', type = str)
+ argsp.add_argument('--build', help = 'Build name to build.',
+ type = str, default='all')
+ argsp.add_argument('--arch', help = 'Build the specific architecture.',
+ type = str)
+ argsp.add_argument('--bsp', help = 'Build the specific BSP.',
+ type = str)
+ argsp.add_argument('--jobs', help = 'Number of jobs to run.',
+ type = int, default = host.cpus())
argsp.add_argument('--dry-run', help = 'Do not run the actual builds.',
action = 'store_true')
@@ -572,7 +1088,8 @@ def run_args(args):
if opts.log is not None:
logf = opts.log
log.default = log.log([logf])
- log.notice('RTEMS Tools Project - RTEMS Kernel BSP Builder, %s' % (version.str()))
+ log.notice(title())
+ log.output(command_line())
if opts.rtems is None:
raise error.general('No RTEMS source provided on the command line')
if opts.prefix is not None:
@@ -587,16 +1104,22 @@ def run_args(args):
config = configuration()
config.load(config_file, opts.build)
- options = { 'stop-on-error' : opts.stop_on_error,
- 'no-clean' : opts.no_clean,
- 'dry-run' : opts.dry_run,
- 'jobs' : 8 }
+ if opts.config_report:
+ log.notice('Configuration Report:')
+ log.notice(config.report())
+ sys.exit(0)
+
+ options = { 'stop-on-error' : opts.stop_on_error,
+ 'no-clean' : opts.no_clean,
+ 'dry-run' : opts.dry_run,
+ 'jobs' : opts.jobs,
+ 'warnings-report' : opts.warnings_report }
b = build(config, rtems_version(), prefix, tools,
path.shell(opts.rtems), build_dir, options)
if opts.arch is not None:
if opts.bsp is not None:
- b.build_arch_bsp(opts.arch, opts.bsp)
+ b.build_bsp(opts.arch, opts.bsp)
else:
b.build_arch(opts.arch)
else:
diff --git a/tester/rtems/rtems-bsps.ini b/tester/rtems/rtems-bsps.ini
index ad63e6d..a88aced 100644
--- a/tester/rtems/rtems-bsps.ini
+++ b/tester/rtems/rtems-bsps.ini
@@ -1,9 +1,9 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2016 Chris Johns (chrisj@rtems.org)
+# Copyright 2016-2017 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
-# This file is part of the RTEMS Tools package in 'rtems-bdp-builder'.
+# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -29,7 +29,7 @@ bsps_arm = altcycv_devkit,
altcycv_devkit_smp,
xilinx_zynq_zc702, xilinx_zynq_zc706, xilinx_zynq_zedboard
bsps_i386 = pc686
-bsps_sparc = erc32
+bsps_sparc = leon2, leon3
#
# Tier 2: no build errors and no unexpected tests failures on hardware and
@@ -40,8 +40,7 @@ archs = arm, sparc
bsps_arm = lm3s6965_qemu,
realview_pbx_a9_qemu, realview_pbx_a9_qemu_smp,
xilinx_zynq_a9_qemu
-bsps_sparc = sis
-
+bsps_sparc = erc32
#
# Tier 3: no build errors, no tests run.
#
@@ -143,7 +142,7 @@ bspopts_pc686 = BSP_PRINT_EXCEPTION_CONTEXT=1
bsps = moxiesim
[sparc]
-bsps = erc32, sis
+bsps = erc32, leon2, leon3
[powerpc]
bsps =
@@ -160,45 +159,65 @@ bsps = epiphany-sim
#
default = all
#
-# The variations, basic is just a BSP build and all is the maximum number of
-# variations.
+# A default build with tests
#
-variations = basic, all
+tests = config:base, config:tests
#
-# The variations, default is Yes and can be overriden in an architecture.
+# A default build without tests
#
-basic = standard
+no-tests = config:base
#
# The all, default is Yes and can be overriden in an architecture.
#
all = debug, profiling, smp, smp-debug,
- posix, no-posix, posix-debug, posix-profiling, posix-smp,
- network, no-network, network-debug, smp-network, smp-network-debug
+ posix, no-posix, posix-debug, posix-profiling,
+ network, no-network, network-debug,
+ smp-network, smp-network-debug
+#
+# The options for each varations.
+#
+debug = config:base, config:tests, config:debug
+profiling = config:base, config:tests, config:profiling
+smp = config:base, config:tests, config:smp
+smp-debug = config:base, config:tests, config:smp, config:debug
+posix = config:base, config:tests, config:posix
+no-posix = config:base, config:tests, config:no-posix
+posix-debug = config:base, config:tests, config:posix, config:debug
+posix-profiling = config:base, config:tests, config:posix, config:profiling
+network = config:base, config:tests, config:network
+no-network = config:base, config:tests, config:no-network
+network-debug = config:base, config:tests, config:network, config:debug
+smp-network = config:base, config:tests, config:smp, config:network
+smp-network-debug = config:base, config:tests, config:smp, config:network,
+ config:debug
+
+#
+# The config section holds the configuration options used in the builds.
+#
+[config]
+#
+# Base set of configure options every build needs.
+#
+base = --target=@ARCH@-rtems@RTEMS_VERSION@
+ --enable-rtemsbsp=@BSP@
+ --prefix=@PREFIX@
#
-# The base build options, tests the default configuration, all variations are
-# added on.
+# Tests.
#
-standard = --target=@ARCH@-rtems@RTEMS_VERSION@
- --enable-rtemsbsp=@BSP@
- --prefix=@PREFIX@
- --enable-tests
+tests = --enable-tests
#
# The options for each varations.
#
debug = --enable-debug
+no-debug = --disable-debug
profiling = --enable-profiling
+no-profiling = --disable-profiling
smp = --enable-smp
-smp-debug = --enable-smp --enable-debug
+no-smp = --disable-smp
posix = --enable-posix
no-posix = --disable-posix
-posix-debug = --enable-posix --enable-debug
-posix-profiling = --enable-posix --enable-profiling
-posix-smp = --enable-posix --enable-smp
network = --enable-networking
no-network = --disable-networking
-network-debug = --enable-debug --enable-networking
-smp-network = --enable-smp --enable-networking
-smp-network-debug = --enable-smp --enable-debug --enable-networking
#
# Define how each profile is to be treated. Note, hardware vs simulator testing