|
|
|
@ -96,6 +96,13 @@ OUTCOME_OK, OUTCOME_WARNING, OUTCOME_ERROR, OUTCOME_UNKNOWN = range(4) |
|
|
|
|
# Translate a commit subject into a valid filename |
|
|
|
|
trans_valid_chars = string.maketrans("/: ", "---") |
|
|
|
|
|
|
|
|
|
CONFIG_FILENAMES = [ |
|
|
|
|
'.config', '.config-spl', '.config-tpl', |
|
|
|
|
'autoconf.mk', 'autoconf-spl.mk', 'autoconf-tpl.mk', |
|
|
|
|
'autoconf.h', 'autoconf-spl.h','autoconf-tpl.h', |
|
|
|
|
'u-boot.cfg', 'u-boot-spl.cfg', 'u-boot-tpl.cfg' |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Builder: |
|
|
|
|
"""Class for building U-Boot for a particular commit. |
|
|
|
@ -166,12 +173,17 @@ class Builder: |
|
|
|
|
value is itself a dictionary: |
|
|
|
|
key: function name |
|
|
|
|
value: Size of function in bytes |
|
|
|
|
config: Dictionary keyed by filename - e.g. '.config'. Each |
|
|
|
|
value is itself a dictionary: |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
""" |
|
|
|
|
def __init__(self, rc, err_lines, sizes, func_sizes): |
|
|
|
|
def __init__(self, rc, err_lines, sizes, func_sizes, config): |
|
|
|
|
self.rc = rc |
|
|
|
|
self.err_lines = err_lines |
|
|
|
|
self.sizes = sizes |
|
|
|
|
self.func_sizes = func_sizes |
|
|
|
|
self.config = config |
|
|
|
|
|
|
|
|
|
def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs, |
|
|
|
|
gnu_make='make', checkout=True, show_unknown=True, step=1, |
|
|
|
@ -254,7 +266,7 @@ class Builder: |
|
|
|
|
|
|
|
|
|
def SetDisplayOptions(self, show_errors=False, show_sizes=False, |
|
|
|
|
show_detail=False, show_bloat=False, |
|
|
|
|
list_error_boards=False): |
|
|
|
|
list_error_boards=False, show_config=False): |
|
|
|
|
"""Setup display options for the builder. |
|
|
|
|
|
|
|
|
|
show_errors: True to show summarised error/warning info |
|
|
|
@ -262,12 +274,14 @@ class Builder: |
|
|
|
|
show_detail: Show detail for each board |
|
|
|
|
show_bloat: Show detail for each function |
|
|
|
|
list_error_boards: Show the boards which caused each error/warning |
|
|
|
|
show_config: Show config deltas |
|
|
|
|
""" |
|
|
|
|
self._show_errors = show_errors |
|
|
|
|
self._show_sizes = show_sizes |
|
|
|
|
self._show_detail = show_detail |
|
|
|
|
self._show_bloat = show_bloat |
|
|
|
|
self._list_error_boards = list_error_boards |
|
|
|
|
self._show_config = show_config |
|
|
|
|
|
|
|
|
|
def _AddTimestamp(self): |
|
|
|
|
"""Add a new timestamp to the list and record the build period. |
|
|
|
@ -335,6 +349,9 @@ class Builder: |
|
|
|
|
cmd = [self.gnu_make] + list(args) |
|
|
|
|
result = command.RunPipe([cmd], capture=True, capture_stderr=True, |
|
|
|
|
cwd=cwd, raise_on_error=False, **kwargs) |
|
|
|
|
if self.verbose_build: |
|
|
|
|
result.stdout = '%s\n' % (' '.join(cmd)) + result.stdout |
|
|
|
|
result.combined = '%s\n' % (' '.join(cmd)) + result.combined |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
def ProcessResult(self, result): |
|
|
|
@ -516,13 +533,50 @@ class Builder: |
|
|
|
|
sym[name] = sym.get(name, 0) + int(size, 16) |
|
|
|
|
return sym |
|
|
|
|
|
|
|
|
|
def GetBuildOutcome(self, commit_upto, target, read_func_sizes): |
|
|
|
|
def _ProcessConfig(self, fname): |
|
|
|
|
"""Read in a .config, autoconf.mk or autoconf.h file |
|
|
|
|
|
|
|
|
|
This function handles all config file types. It ignores comments and |
|
|
|
|
any #defines which don't start with CONFIG_. |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
fname: Filename to read |
|
|
|
|
|
|
|
|
|
Returns: |
|
|
|
|
Dictionary: |
|
|
|
|
key: Config name (e.g. CONFIG_DM) |
|
|
|
|
value: Config value (e.g. 1) |
|
|
|
|
""" |
|
|
|
|
config = {} |
|
|
|
|
if os.path.exists(fname): |
|
|
|
|
with open(fname) as fd: |
|
|
|
|
for line in fd: |
|
|
|
|
line = line.strip() |
|
|
|
|
if line.startswith('#define'): |
|
|
|
|
values = line[8:].split(' ', 1) |
|
|
|
|
if len(values) > 1: |
|
|
|
|
key, value = values |
|
|
|
|
else: |
|
|
|
|
key = values[0] |
|
|
|
|
value = '' |
|
|
|
|
if not key.startswith('CONFIG_'): |
|
|
|
|
continue |
|
|
|
|
elif not line or line[0] in ['#', '*', '/']: |
|
|
|
|
continue |
|
|
|
|
else: |
|
|
|
|
key, value = line.split('=', 1) |
|
|
|
|
config[key] = value |
|
|
|
|
return config |
|
|
|
|
|
|
|
|
|
def GetBuildOutcome(self, commit_upto, target, read_func_sizes, |
|
|
|
|
read_config): |
|
|
|
|
"""Work out the outcome of a build. |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
commit_upto: Commit number to check (0..n-1) |
|
|
|
|
target: Target board to check |
|
|
|
|
read_func_sizes: True to read function size information |
|
|
|
|
read_config: True to read .config and autoconf.h files |
|
|
|
|
|
|
|
|
|
Returns: |
|
|
|
|
Outcome object |
|
|
|
@ -531,6 +585,7 @@ class Builder: |
|
|
|
|
sizes_file = self.GetSizesFile(commit_upto, target) |
|
|
|
|
sizes = {} |
|
|
|
|
func_sizes = {} |
|
|
|
|
config = {} |
|
|
|
|
if os.path.exists(done_file): |
|
|
|
|
with open(done_file, 'r') as fd: |
|
|
|
|
return_code = int(fd.readline()) |
|
|
|
@ -574,17 +629,25 @@ class Builder: |
|
|
|
|
'') |
|
|
|
|
func_sizes[dict_name] = self.ReadFuncSizes(fname, fd) |
|
|
|
|
|
|
|
|
|
return Builder.Outcome(rc, err_lines, sizes, func_sizes) |
|
|
|
|
if read_config: |
|
|
|
|
output_dir = self.GetBuildDir(commit_upto, target) |
|
|
|
|
for name in CONFIG_FILENAMES: |
|
|
|
|
fname = os.path.join(output_dir, name) |
|
|
|
|
config[name] = self._ProcessConfig(fname) |
|
|
|
|
|
|
|
|
|
return Builder.Outcome(rc, err_lines, sizes, func_sizes, config) |
|
|
|
|
|
|
|
|
|
return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}) |
|
|
|
|
return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}) |
|
|
|
|
|
|
|
|
|
def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes): |
|
|
|
|
def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes, |
|
|
|
|
read_config): |
|
|
|
|
"""Calculate a summary of the results of building a commit. |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
board_selected: Dict containing boards to summarise |
|
|
|
|
commit_upto: Commit number to summarize (0..self.count-1) |
|
|
|
|
read_func_sizes: True to read function size information |
|
|
|
|
read_config: True to read .config and autoconf.h files |
|
|
|
|
|
|
|
|
|
Returns: |
|
|
|
|
Tuple: |
|
|
|
@ -596,6 +659,10 @@ class Builder: |
|
|
|
|
List containing a summary of warning lines |
|
|
|
|
Dict keyed by error line, containing a list of the Board |
|
|
|
|
objects with that warning |
|
|
|
|
Dictionary keyed by filename - e.g. '.config'. Each |
|
|
|
|
value is itself a dictionary: |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
""" |
|
|
|
|
def AddLine(lines_summary, lines_boards, line, board): |
|
|
|
|
line = line.rstrip() |
|
|
|
@ -610,10 +677,13 @@ class Builder: |
|
|
|
|
err_lines_boards = {} |
|
|
|
|
warn_lines_summary = [] |
|
|
|
|
warn_lines_boards = {} |
|
|
|
|
config = {} |
|
|
|
|
for fname in CONFIG_FILENAMES: |
|
|
|
|
config[fname] = {} |
|
|
|
|
|
|
|
|
|
for board in boards_selected.itervalues(): |
|
|
|
|
outcome = self.GetBuildOutcome(commit_upto, board.target, |
|
|
|
|
read_func_sizes) |
|
|
|
|
read_func_sizes, read_config) |
|
|
|
|
board_dict[board.target] = outcome |
|
|
|
|
last_func = None |
|
|
|
|
last_was_warning = False |
|
|
|
@ -639,8 +709,14 @@ class Builder: |
|
|
|
|
line, board) |
|
|
|
|
last_was_warning = is_warning |
|
|
|
|
last_func = None |
|
|
|
|
for fname in CONFIG_FILENAMES: |
|
|
|
|
config[fname] = {} |
|
|
|
|
if outcome.config: |
|
|
|
|
for key, value in outcome.config[fname].iteritems(): |
|
|
|
|
config[fname][key] = value |
|
|
|
|
|
|
|
|
|
return (board_dict, err_lines_summary, err_lines_boards, |
|
|
|
|
warn_lines_summary, warn_lines_boards) |
|
|
|
|
warn_lines_summary, warn_lines_boards, config) |
|
|
|
|
|
|
|
|
|
def AddOutcome(self, board_dict, arch_list, changes, char, color): |
|
|
|
|
"""Add an output to our list of outcomes for each architecture |
|
|
|
@ -693,11 +769,14 @@ class Builder: |
|
|
|
|
""" |
|
|
|
|
self._base_board_dict = {} |
|
|
|
|
for board in board_selected: |
|
|
|
|
self._base_board_dict[board] = Builder.Outcome(0, [], [], {}) |
|
|
|
|
self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {}) |
|
|
|
|
self._base_err_lines = [] |
|
|
|
|
self._base_warn_lines = [] |
|
|
|
|
self._base_err_line_boards = {} |
|
|
|
|
self._base_warn_line_boards = {} |
|
|
|
|
self._base_config = {} |
|
|
|
|
for fname in CONFIG_FILENAMES: |
|
|
|
|
self._base_config[fname] = {} |
|
|
|
|
|
|
|
|
|
def PrintFuncSizeDetail(self, fname, old, new): |
|
|
|
|
grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 |
|
|
|
@ -892,7 +971,8 @@ class Builder: |
|
|
|
|
|
|
|
|
|
def PrintResultSummary(self, board_selected, board_dict, err_lines, |
|
|
|
|
err_line_boards, warn_lines, warn_line_boards, |
|
|
|
|
show_sizes, show_detail, show_bloat): |
|
|
|
|
config, show_sizes, show_detail, show_bloat, |
|
|
|
|
show_config): |
|
|
|
|
"""Compare results with the base results and display delta. |
|
|
|
|
|
|
|
|
|
Only boards mentioned in board_selected will be considered. This |
|
|
|
@ -913,9 +993,14 @@ class Builder: |
|
|
|
|
none, or we don't want to print errors |
|
|
|
|
warn_line_boards: Dict keyed by warning line, containing a list of |
|
|
|
|
the Board objects with that warning |
|
|
|
|
config: Dictionary keyed by filename - e.g. '.config'. Each |
|
|
|
|
value is itself a dictionary: |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
show_sizes: Show image size deltas |
|
|
|
|
show_detail: Show detail for each board |
|
|
|
|
show_bloat: Show detail for each function |
|
|
|
|
show_config: Show config changes |
|
|
|
|
""" |
|
|
|
|
def _BoardList(line, line_boards): |
|
|
|
|
"""Helper function to get a line of boards containing a line |
|
|
|
@ -950,6 +1035,48 @@ class Builder: |
|
|
|
|
_BoardList(line, base_line_boards) + line) |
|
|
|
|
return better_lines, worse_lines |
|
|
|
|
|
|
|
|
|
def _CalcConfig(delta, name, config): |
|
|
|
|
"""Calculate configuration changes |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
delta: Type of the delta, e.g. '+' |
|
|
|
|
name: name of the file which changed (e.g. .config) |
|
|
|
|
config: configuration change dictionary |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
Returns: |
|
|
|
|
String containing the configuration changes which can be |
|
|
|
|
printed |
|
|
|
|
""" |
|
|
|
|
out = '' |
|
|
|
|
for key in sorted(config.keys()): |
|
|
|
|
out += '%s=%s ' % (key, config[key]) |
|
|
|
|
return '%5s %s: %s' % (delta, name, out) |
|
|
|
|
|
|
|
|
|
def _ShowConfig(name, config_plus, config_minus, config_change): |
|
|
|
|
"""Show changes in configuration |
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
config_plus: configurations added, dictionary |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
config_minus: configurations removed, dictionary |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
config_change: configurations changed, dictionary |
|
|
|
|
key: config name |
|
|
|
|
value: config value |
|
|
|
|
""" |
|
|
|
|
if config_plus: |
|
|
|
|
Print(_CalcConfig('+', name, config_plus), |
|
|
|
|
colour=self.col.GREEN) |
|
|
|
|
if config_minus: |
|
|
|
|
Print(_CalcConfig('-', name, config_minus), |
|
|
|
|
colour=self.col.RED) |
|
|
|
|
if config_change: |
|
|
|
|
Print(_CalcConfig('+/-', name, config_change), |
|
|
|
|
colour=self.col.YELLOW) |
|
|
|
|
|
|
|
|
|
better = [] # List of boards fixed since last commit |
|
|
|
|
worse = [] # List of new broken boards since last commit |
|
|
|
|
new = [] # List of boards that didn't exist last time |
|
|
|
@ -1010,12 +1137,42 @@ class Builder: |
|
|
|
|
self.PrintSizeSummary(board_selected, board_dict, show_detail, |
|
|
|
|
show_bloat) |
|
|
|
|
|
|
|
|
|
if show_config: |
|
|
|
|
all_config_plus = {} |
|
|
|
|
all_config_minus = {} |
|
|
|
|
all_config_change = {} |
|
|
|
|
for name in CONFIG_FILENAMES: |
|
|
|
|
if not config[name]: |
|
|
|
|
continue |
|
|
|
|
config_plus = {} |
|
|
|
|
config_minus = {} |
|
|
|
|
config_change = {} |
|
|
|
|
base = self._base_config[name] |
|
|
|
|
for key, value in config[name].iteritems(): |
|
|
|
|
if key not in base: |
|
|
|
|
config_plus[key] = value |
|
|
|
|
all_config_plus[key] = value |
|
|
|
|
for key, value in base.iteritems(): |
|
|
|
|
if key not in config[name]: |
|
|
|
|
config_minus[key] = value |
|
|
|
|
all_config_minus[key] = value |
|
|
|
|
for key, value in base.iteritems(): |
|
|
|
|
new_value = base[key] |
|
|
|
|
if key in config[name] and value != new_value: |
|
|
|
|
desc = '%s -> %s' % (value, new_value) |
|
|
|
|
config_change[key] = desc |
|
|
|
|
all_config_change[key] = desc |
|
|
|
|
_ShowConfig(name, config_plus, config_minus, config_change) |
|
|
|
|
_ShowConfig('all', all_config_plus, all_config_minus, |
|
|
|
|
all_config_change) |
|
|
|
|
|
|
|
|
|
# Save our updated information for the next call to this function |
|
|
|
|
self._base_board_dict = board_dict |
|
|
|
|
self._base_err_lines = err_lines |
|
|
|
|
self._base_warn_lines = warn_lines |
|
|
|
|
self._base_err_line_boards = err_line_boards |
|
|
|
|
self._base_warn_line_boards = warn_line_boards |
|
|
|
|
self._base_config = config |
|
|
|
|
|
|
|
|
|
# Get a list of boards that did not get built, if needed |
|
|
|
|
not_built = [] |
|
|
|
@ -1028,9 +1185,10 @@ class Builder: |
|
|
|
|
|
|
|
|
|
def ProduceResultSummary(self, commit_upto, commits, board_selected): |
|
|
|
|
(board_dict, err_lines, err_line_boards, warn_lines, |
|
|
|
|
warn_line_boards) = self.GetResultSummary( |
|
|
|
|
warn_line_boards, config) = self.GetResultSummary( |
|
|
|
|
board_selected, commit_upto, |
|
|
|
|
read_func_sizes=self._show_bloat) |
|
|
|
|
read_func_sizes=self._show_bloat, |
|
|
|
|
read_config=self._show_config) |
|
|
|
|
if commits: |
|
|
|
|
msg = '%02d: %s' % (commit_upto + 1, |
|
|
|
|
commits[commit_upto].subject) |
|
|
|
@ -1038,7 +1196,8 @@ class Builder: |
|
|
|
|
self.PrintResultSummary(board_selected, board_dict, |
|
|
|
|
err_lines if self._show_errors else [], err_line_boards, |
|
|
|
|
warn_lines if self._show_errors else [], warn_line_boards, |
|
|
|
|
self._show_sizes, self._show_detail, self._show_bloat) |
|
|
|
|
config, self._show_sizes, self._show_detail, |
|
|
|
|
self._show_bloat, self._show_config) |
|
|
|
|
|
|
|
|
|
def ShowSummary(self, commits, board_selected): |
|
|
|
|
"""Show a build summary for U-Boot for a given board list. |
|
|
|
|