[PATCH v5 12/13] buildman: Allow adjusting board config on the fly

Simon Glass
2022-01-22T12:09:48Z

Add a -a option to specify changes to the config before the build
commences. For example

buildman -a ~CONFIG_CMDLINE

disables CONFIG_CMDLINE before doing the build.

This makes it easier to try things out as well as to write tests without
creating a new board or manually manging the .config file.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

(no changes since v1)

tools/buildman/README | 49 +++++++++++++++++++++++++++++++++
tools/buildman/builder.py | 14 +++++++++-
tools/buildman/builderthread.py | 30 ++++++++++++++++----
tools/buildman/cmdline.py | 2 ++
tools/buildman/control.py | 6 +++-
5 files changed, 93 insertions(+), 8 deletions(-)

diff --git a/tools/buildman/README b/tools/buildman/README
index ce277884326..bafb3b065ce 100644
--- a/tools/buildman/README
+++ b/tools/buildman/README
@@ -1095,6 +1095,55 @@ This will write the full build into /tmp/build including object files. You must
specify the output directory with -o when using -w.

+Changing the configuration
+==========================
+
+Sometimes it is useful to change the CONFIG options for a build on the fly. This
+can be used to build a board (or multiple) with a few changes to see the impact.
+The -a option supports this:
+
+ -a <cfg>
+
+where <cfg> is a CONFIG option (with or without the CONFIG_ prefix) to enable.
+For example:
+
+ buildman -a CMD_SETEXPR_FMT
+
+will build with CONFIG_CMD_SETEXPR_FMT enabled.
+
+You can disable options by preceding them with tilde (~). You can specify the
+-a option multiple times:
+
+ buildman -a CMD_SETEXPR_FMT -a ~CMDLINE
+
+Some options have values, in which case you can change them:
+
+ buildman -a 'BOOTCOMMAND="echo hello"' CONFIG_SYS_LOAD_ADDR=0x1000
+
+Note that you must put quotes around string options and the whole thing must be
+in single quotes, to make sure the shell leave it alone.
+
+If you try to set an option that does not exist, or that cannot be changed for
+some other reason (e.g. it is 'selected' by another option), then buildman
+shows an error:
+
+ buildman --board sandbox -a FRED
+ Building current source for 1 boards (1 thread, 32 jobs per thread)
+ 0 0 0 /1 -1 (starting)errs
+ Some CONFIG adjustments did not take effect. This may be because
+ the request CONFIGs do not exist or conflict with others.
+
+ Failed adjustments:
+
+ FRED Missing expected line: CONFIG_FRED=y
+
+
+One major caveat with this feature with branches (-b) is that buildman does not
+name the output directories differently when you change the configuration, so
+doing the same build again with different configuration will not trigger a
+rebuild. You can use -f to work around that.
+
+
Other options
=============

diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py
index 122f0d14065..720bbb2cf4d 100644
--- a/tools/buildman/builder.py
+++ b/tools/buildman/builder.py
@@ -250,7 +250,7 @@ class Builder:
mrproper=False, per_board_out_dir=False,
config_only=False, squash_config_y=False,
warnings_as_errors=False, work_in_output=False,
- test_thread_exceptions=False):
+ test_thread_exceptions=False, adjust_cfg=None):
"""Create a new Builder object

Args:
@@ -280,6 +280,15 @@ class Builder:
test_thread_exceptions: Uses for tests only, True to make the
threads raise an exception instead of reporting their result.
This simulates a failure in the code somewhere
+ adjust_cfg_list (list of str): List of changes to make to .config
+ file before building. Each is one of (where C is the config
+ option with or without the CONFIG_ prefix)
+
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig
+
"""
self.toolchains = toolchains
self.base_dir = base_dir
@@ -315,6 +324,8 @@ class Builder:
self.squash_config_y = squash_config_y
self.config_filenames = BASE_CONFIG_FILENAMES
self.work_in_output = work_in_output
+ self.adjust_cfg = adjust_cfg
+
if not self.squash_config_y:
self.config_filenames += EXTRA_CONFIG_FILENAMES
self._terminated = False
@@ -1747,6 +1758,7 @@ class Builder:
job.commits = commits
job.keep_outputs = keep_outputs
job.work_in_output = self.work_in_output
+ job.adjust_cfg = self.adjust_cfg
job.step = self._step
if self.num_threads:
self.queue.put(job)
diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py
index 0faa3ac9385..ecb285c0bfa 100644
--- a/tools/buildman/builderthread.py
+++ b/tools/buildman/builderthread.py
@@ -9,6 +9,7 @@ import shutil
import sys
import threading

+from buildman import cfgutil
from patman import command
from patman import gitutil

@@ -130,7 +131,8 @@ class BuilderThread(threading.Thread):
**kwargs)

def RunCommit(self, commit_upto, brd, work_dir, do_config, config_only,
- force_build, force_build_failures, work_in_output):
+ force_build, force_build_failures, work_in_output,
+ adjust_cfg):
"""Build a particular commit.

If the build is already done, and we are not forcing a build, we skip
@@ -147,6 +149,13 @@ class BuilderThread(threading.Thread):
failure
work_in_output: Use the output directory as the work directory and
don't write to a separate output directory.
+ adjust_cfg (list of str): List of changes to make to .config file
+ before building. Each is one of (where C is either CONFIG_xxx
+ or just xxx):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig

Returns:
tuple containing:
@@ -261,7 +270,8 @@ class BuilderThread(threading.Thread):
os.remove(fname)

# If we need to reconfigure, do that now
- if do_config:
+ cfg_file = os.path.join(out_dir, '.config')
+ if do_config or adjust_cfg:
config_out = ''
if self.mrproper:
result = self.Make(commit, brd, 'mrproper', cwd,
@@ -271,11 +281,19 @@ class BuilderThread(threading.Thread):
*(args + config_args), env=env)
config_out += result.combined
do_config = False # No need to configure next time
+ if adjust_cfg:
+ cfgutil.adjust_cfg_file(cfg_file, adjust_cfg)
if result.return_code == 0:
if config_only:
args.append('cfg')
result = self.Make(commit, brd, 'build', cwd, *args,
env=env)
+ if adjust_cfg:
+ errs = cfgutil.check_cfg_file(cfg_file, adjust_cfg)
+ if errs:
+ print('errs', errs)
+ result.stderr += errs
+ result.return_code = 1
result.stderr = result.stderr.replace(src_dir + '/', '')
if self.builder.verbose_build:
result.stdout = config_out + result.stdout
@@ -486,7 +504,7 @@ class BuilderThread(threading.Thread):
work_dir, do_config, self.builder.config_only,
force_build or self.builder.force_build,
self.builder.force_build_failures,
- work_in_output=job.work_in_output)
+ job.work_in_output, job.adjust_cfg)
failed = result.return_code or result.stderr
did_config = do_config
if failed and not do_config:
@@ -495,7 +513,7 @@ class BuilderThread(threading.Thread):
if self.builder.force_config_on_failure:
result, request_config = self.RunCommit(commit_upto,
brd, work_dir, True, False, True, False,
- work_in_output=job.work_in_output)
+ job.work_in_output, job.adjust_cfg)
did_config = True
if not self.builder.force_reconfig:
do_config = request_config
@@ -540,8 +558,8 @@ class BuilderThread(threading.Thread):
# Just build the currently checked-out build
result, request_config = self.RunCommit(None, brd, work_dir, True,
self.builder.config_only, True,
- self.builder.force_build_failures,
- work_in_output=job.work_in_output)
+ self.builder.force_build_failures, job.work_in_output,
+ job.adjust_cfg)
result.commit_upto = 0
self._WriteResult(result, job.keep_outputs, job.work_in_output)
self._SendResult(result)
diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py
index 092a82f4e9c..8586bdf3b58 100644
--- a/tools/buildman/cmdline.py
+++ b/tools/buildman/cmdline.py
@@ -13,6 +13,8 @@ def ParseArgs():
args: command lin arguments
"""
parser = OptionParser()
+ parser.add_option('-a', '--adjust-cfg', type=str, action='append',
+ help='Adjust the Kconfig settings in .config before building')
parser.add_option('-A', '--print-prefix', action='store_true',
help='Print the tool-chain prefix for a board (CROSS_COMPILE=)')
parser.add_option('-b', '--branch', type='string',
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index fd9664c85d8..eee81130663 100644
--- a/tools/buildman/control.py
+++ b/tools/buildman/control.py
@@ -10,6 +10,7 @@ import sys

from buildman import board
from buildman import bsettings
+from buildman import cfgutil
from buildman import toolchain
from buildman.builder import Builder
from patman import command
@@ -321,6 +322,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
output_dir = os.path.join(options.output_dir, dirname)
if clean_dir and os.path.exists(output_dir):
shutil.rmtree(output_dir)
+ adjust_cfg = cfgutil.convert_list_to_dict(options.adjust_cfg)
+
builder = Builder(toolchains, output_dir, options.git_dir,
options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
show_unknown=options.show_unknown, step=options.step,
@@ -332,7 +335,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
squash_config_y=not options.preserve_config_y,
warnings_as_errors=options.warnings_as_errors,
work_in_output=options.work_in_output,
- test_thread_exceptions=test_thread_exceptions)
+ test_thread_exceptions=test_thread_exceptions,
+ adjust_cfg=adjust_cfg)
builder.force_config_on_failure = not options.quick
if make_func:
builder.do_make = make_func
--
2.35.0.rc0.227.g00780c9af4-goog