]> git.sur5r.net Git - u-boot/blobdiff - tools/buildman/builder.py
dtoc: Add functions to add integer properties
[u-boot] / tools / buildman / builder.py
index e27a28577c2547a134acf65f392b05668137d0da..a5a2ffdfdf2c76d86fb78d765d9977245da652ac 100644 (file)
@@ -1,9 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
 # Copyright (c) 2013 The Chromium OS Authors.
 #
 # Bloat-o-meter code used here Copyright 2004 Matt Mackall <mpm@selenic.com>
 #
-# SPDX-License-Identifier:     GPL-2.0+
-#
 
 import collections
 from datetime import datetime, timedelta
@@ -95,22 +94,26 @@ u-boot/             source directory
 # Possible build outcomes
 OUTCOME_OK, OUTCOME_WARNING, OUTCOME_ERROR, OUTCOME_UNKNOWN = range(4)
 
-# Translate a commit subject into a valid filename
-trans_valid_chars = string.maketrans("/: ", "---")
+# Translate a commit subject into a valid filename (and handle unicode)
+trans_valid_chars = string.maketrans('/: ', '---')
+trans_valid_chars = trans_valid_chars.decode('latin-1')
+
+BASE_CONFIG_FILENAMES = [
+    'u-boot.cfg', 'u-boot-spl.cfg', 'u-boot-tpl.cfg'
+]
 
-CONFIG_FILENAMES = [
+EXTRA_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 Config:
     """Holds information about configuration settings for a board."""
-    def __init__(self, target):
+    def __init__(self, config_filename, target):
         self.target = target
         self.config = {}
-        for fname in CONFIG_FILENAMES:
+        for fname in config_filename:
             self.config[fname] = {}
 
     def Add(self, fname, key, value):
@@ -124,6 +127,15 @@ class Config:
                 val = val ^ hash(key) & hash(value)
         return val
 
+class Environment:
+    """Holds information about environment variables for a board."""
+    def __init__(self, target):
+        self.target = target
+        self.environment = {}
+
+    def Add(self, key, value):
+        self.environment[key] = value
+
 class Builder:
     """Class for building U-Boot for a particular commit.
 
@@ -196,18 +208,24 @@ class Builder:
                     value is itself a dictionary:
                         key: config name
                         value: config value
+            environment: Dictionary keyed by environment variable, Each
+                     value is the value of environment variable.
         """
-        def __init__(self, rc, err_lines, sizes, func_sizes, config):
+        def __init__(self, rc, err_lines, sizes, func_sizes, config,
+                     environment):
             self.rc = rc
             self.err_lines = err_lines
             self.sizes = sizes
             self.func_sizes = func_sizes
             self.config = config
+            self.environment = environment
 
     def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs,
                  gnu_make='make', checkout=True, show_unknown=True, step=1,
                  no_subdirs=False, full_path=False, verbose_build=False,
-                 incremental=False, per_board_out_dir=False):
+                 incremental=False, per_board_out_dir=False,
+                 config_only=False, squash_config_y=False,
+                 warnings_as_errors=False):
         """Create a new Builder object
 
         Args:
@@ -230,6 +248,9 @@ class Builder:
                 mrproper when configuring
             per_board_out_dir: Build in a separate persistent directory per
                 board rather than a thread-specific directory
+            config_only: Only configure each build, don't build it
+            squash_config_y: Convert CONFIG options with the value 'y' to '1'
+            warnings_as_errors: Treat all compiler warnings as errors
         """
         self.toolchains = toolchains
         self.base_dir = base_dir
@@ -257,7 +278,13 @@ class Builder:
         self.no_subdirs = no_subdirs
         self.full_path = full_path
         self.verbose_build = verbose_build
+        self.config_only = config_only
+        self.squash_config_y = squash_config_y
+        self.config_filenames = BASE_CONFIG_FILENAMES
+        if not self.squash_config_y:
+            self.config_filenames += EXTRA_CONFIG_FILENAMES
 
+        self.warnings_as_errors = warnings_as_errors
         self.col = terminal.Color()
 
         self._re_function = re.compile('(.*): In function.*')
@@ -296,7 +323,8 @@ class Builder:
 
     def SetDisplayOptions(self, show_errors=False, show_sizes=False,
                           show_detail=False, show_bloat=False,
-                          list_error_boards=False, show_config=False):
+                          list_error_boards=False, show_config=False,
+                          show_environment=False):
         """Setup display options for the builder.
 
         show_errors: True to show summarised error/warning info
@@ -305,6 +333,7 @@ class Builder:
         show_bloat: Show detail for each function
         list_error_boards: Show the boards which caused each error/warning
         show_config: Show config deltas
+        show_environment: Show environment deltas
         """
         self._show_errors = show_errors
         self._show_sizes = show_sizes
@@ -312,6 +341,7 @@ class Builder:
         self._show_bloat = show_bloat
         self._list_error_boards = list_error_boards
         self._show_config = show_config
+        self._show_environment = show_environment
 
     def _AddTimestamp(self):
         """Add a new timestamp to the list and record the build period.
@@ -432,7 +462,7 @@ class Builder:
 
         name += target
         Print(line + name, newline=False)
-        length = 14 + len(name)
+        length = 16 + len(name)
         self.ClearLine(length)
 
     def _GetOutputDir(self, commit_upto):
@@ -583,18 +613,45 @@ class Builder:
                             key, value = values
                         else:
                             key = values[0]
-                            value = ''
+                            value = '1' if self.squash_config_y else ''
                         if not key.startswith('CONFIG_'):
                             continue
                     elif not line or line[0] in ['#', '*', '/']:
                         continue
                     else:
                         key, value = line.split('=', 1)
+                    if self.squash_config_y and value == 'y':
+                        value = '1'
                     config[key] = value
         return config
 
+    def _ProcessEnvironment(self, fname):
+        """Read in a uboot.env file
+
+        This function reads in environment variables from a file.
+
+        Args:
+            fname: Filename to read
+
+        Returns:
+            Dictionary:
+                key: environment variable (e.g. bootlimit)
+                value: value of environment variable (e.g. 1)
+        """
+        environment = {}
+        if os.path.exists(fname):
+            with open(fname) as fd:
+                for line in fd.read().split('\0'):
+                    try:
+                        key, value = line.split('=', 1)
+                        environment[key] = value
+                    except ValueError:
+                        # ignore lines we can't parse
+                        pass
+        return environment
+
     def GetBuildOutcome(self, commit_upto, target, read_func_sizes,
-                        read_config):
+                        read_config, read_environment):
         """Work out the outcome of a build.
 
         Args:
@@ -602,6 +659,7 @@ class Builder:
             target: Target board to check
             read_func_sizes: True to read function size information
             read_config: True to read .config and autoconf.h files
+            read_environment: True to read uboot.env files
 
         Returns:
             Outcome object
@@ -611,6 +669,7 @@ class Builder:
         sizes = {}
         func_sizes = {}
         config = {}
+        environment = {}
         if os.path.exists(done_file):
             with open(done_file, 'r') as fd:
                 return_code = int(fd.readline())
@@ -656,16 +715,22 @@ class Builder:
 
             if read_config:
                 output_dir = self.GetBuildDir(commit_upto, target)
-                for name in CONFIG_FILENAMES:
+                for name in self.config_filenames:
                     fname = os.path.join(output_dir, name)
                     config[name] = self._ProcessConfig(fname)
 
-            return Builder.Outcome(rc, err_lines, sizes, func_sizes, config)
+            if read_environment:
+                output_dir = self.GetBuildDir(commit_upto, target)
+                fname = os.path.join(output_dir, 'uboot.env')
+                environment = self._ProcessEnvironment(fname)
+
+            return Builder.Outcome(rc, err_lines, sizes, func_sizes, config,
+                                   environment)
 
-        return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {})
+        return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}, {})
 
     def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes,
-                         read_config):
+                         read_config, read_environment):
         """Calculate a summary of the results of building a commit.
 
         Args:
@@ -673,6 +738,7 @@ class Builder:
             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
+            read_environment: True to read uboot.env files
 
         Returns:
             Tuple:
@@ -689,6 +755,9 @@ class Builder:
                     value is itself a dictionary:
                         key: config name
                         value: config value
+                Dictionary keyed by board.target. Each value is a dictionary:
+                    key: environment variable
+                    value: value of environment variable
         """
         def AddLine(lines_summary, lines_boards, line, board):
             line = line.rstrip()
@@ -704,10 +773,12 @@ class Builder:
         warn_lines_summary = []
         warn_lines_boards = {}
         config = {}
+        environment = {}
 
         for board in boards_selected.itervalues():
             outcome = self.GetBuildOutcome(commit_upto, board.target,
-                                           read_func_sizes, read_config)
+                                           read_func_sizes, read_config,
+                                           read_environment)
             board_dict[board.target] = outcome
             last_func = None
             last_was_warning = False
@@ -733,15 +804,21 @@ class Builder:
                                     line, board)
                         last_was_warning = is_warning
                         last_func = None
-            tconfig = Config(board.target)
-            for fname in CONFIG_FILENAMES:
+            tconfig = Config(self.config_filenames, board.target)
+            for fname in self.config_filenames:
                 if outcome.config:
                     for key, value in outcome.config[fname].iteritems():
                         tconfig.Add(fname, key, value)
             config[board.target] = tconfig
 
+            tenvironment = Environment(board.target)
+            if outcome.environment:
+                for key, value in outcome.environment.iteritems():
+                    tenvironment.Add(key, value)
+            environment[board.target] = tenvironment
+
         return (board_dict, err_lines_summary, err_lines_boards,
-                warn_lines_summary, warn_lines_boards, config)
+                warn_lines_summary, warn_lines_boards, config, environment)
 
     def AddOutcome(self, board_dict, arch_list, changes, char, color):
         """Add an output to our list of outcomes for each architecture
@@ -794,12 +871,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 = None
+        self._base_environment = None
 
     def PrintFuncSizeDetail(self, fname, old, new):
         grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
@@ -833,7 +912,7 @@ class Builder:
         delta.reverse()
 
         args = [add, -remove, grow, -shrink, up, -down, up - down]
-        if max(args) == 0:
+        if max(args) == 0 and min(args) == 0:
             return
         args = [self.ColourNum(x) for x in args]
         indent = ' ' * 15
@@ -994,8 +1073,8 @@ class Builder:
 
     def PrintResultSummary(self, board_selected, board_dict, err_lines,
                            err_line_boards, warn_lines, warn_line_boards,
-                           config, show_sizes, show_detail, show_bloat,
-                           show_config):
+                           config, environment, show_sizes, show_detail,
+                           show_bloat, show_config, show_environment):
         """Compare results with the base results and display delta.
 
         Only boards mentioned in board_selected will be considered. This
@@ -1020,10 +1099,13 @@ class Builder:
                     value is itself a dictionary:
                         key: config name
                         value: config value
+            environment: Dictionary keyed by environment variable, Each
+                     value is the value of environment variable.
             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
+            show_environment: Show environment changes
         """
         def _BoardList(line, line_boards):
             """Helper function to get a line of boards containing a line
@@ -1172,6 +1254,36 @@ class Builder:
             self.PrintSizeSummary(board_selected, board_dict, show_detail,
                                   show_bloat)
 
+        if show_environment and self._base_environment:
+            lines = []
+
+            for target in board_dict:
+                if target not in board_selected:
+                    continue
+
+                tbase = self._base_environment[target]
+                tenvironment = environment[target]
+                environment_plus = {}
+                environment_minus = {}
+                environment_change = {}
+                base = tbase.environment
+                for key, value in tenvironment.environment.iteritems():
+                    if key not in base:
+                        environment_plus[key] = value
+                for key, value in base.iteritems():
+                    if key not in tenvironment.environment:
+                        environment_minus[key] = value
+                for key, value in base.iteritems():
+                    new_value = tenvironment.environment.get(key)
+                    if new_value and value != new_value:
+                        desc = '%s -> %s' % (value, new_value)
+                        environment_change[key] = desc
+
+                _AddConfig(lines, target, environment_plus, environment_minus,
+                           environment_change)
+
+            _OutputConfigInfo(lines)
+
         if show_config and self._base_config:
             summary = {}
             arch_config_plus = {}
@@ -1190,7 +1302,7 @@ class Builder:
                 arch_config_plus[arch] = {}
                 arch_config_minus[arch] = {}
                 arch_config_change[arch] = {}
-                for name in CONFIG_FILENAMES:
+                for name in self.config_filenames:
                     arch_config_plus[arch][name] = {}
                     arch_config_minus[arch][name] = {}
                     arch_config_change[arch][name] = {}
@@ -1207,7 +1319,7 @@ class Builder:
                 tbase = self._base_config[target]
                 tconfig = config[target]
                 lines = []
-                for name in CONFIG_FILENAMES:
+                for name in self.config_filenames:
                     if not tconfig.config[name]:
                         continue
                     config_plus = {}
@@ -1251,7 +1363,7 @@ class Builder:
                 all_plus = {}
                 all_minus = {}
                 all_change = {}
-                for name in CONFIG_FILENAMES:
+                for name in self.config_filenames:
                     all_plus.update(arch_config_plus[arch][name])
                     all_minus.update(arch_config_minus[arch][name])
                     all_change.update(arch_config_change[arch][name])
@@ -1278,6 +1390,7 @@ class Builder:
         self._base_err_line_boards = err_line_boards
         self._base_warn_line_boards = warn_line_boards
         self._base_config = config
+        self._base_environment = environment
 
         # Get a list of boards that did not get built, if needed
         not_built = []
@@ -1290,10 +1403,11 @@ class Builder:
 
     def ProduceResultSummary(self, commit_upto, commits, board_selected):
             (board_dict, err_lines, err_line_boards, warn_lines,
-                    warn_line_boards, config) = self.GetResultSummary(
+             warn_line_boards, config, environment) = self.GetResultSummary(
                     board_selected, commit_upto,
                     read_func_sizes=self._show_bloat,
-                    read_config=self._show_config)
+                    read_config=self._show_config,
+                    read_environment=self._show_environment)
             if commits:
                 msg = '%02d: %s' % (commit_upto + 1,
                         commits[commit_upto].subject)
@@ -1301,8 +1415,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,
-                    config, self._show_sizes, self._show_detail,
-                    self._show_bloat, self._show_config)
+                    config, environment, self._show_sizes, self._show_detail,
+                    self._show_bloat, self._show_config, self._show_environment)
 
     def ShowSummary(self, commits, board_selected):
         """Show a build summary for U-Boot for a given board list.