This is used by some boards in U-Boot and is a convenient way to deal with common settings where using a Kconfig files is not desirable.
Detect #include files and process them as if they were part of the original file. Signed-off-by: Simon Glass <s...@chromium.org> Fixes: https://source.denx.de/u-boot/custodians/u-boot-dm/-/issues/30 --- tools/buildman/boards.py | 25 ++++++++++++++++++++++++- tools/buildman/buildman.rst | 24 ++++++++++++++++++++++++ tools/buildman/func_test.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/tools/buildman/boards.py b/tools/buildman/boards.py index 3c2822715f3..9e7b486656b 100644 --- a/tools/buildman/boards.py +++ b/tools/buildman/boards.py @@ -19,7 +19,10 @@ import time from buildman import board from buildman import kconfiglib +from u_boot_pylib import command from u_boot_pylib.terminal import print_clear, tprint +from u_boot_pylib import tools +from u_boot_pylib import tout ### constant variables ### OUTPUT_FILE = 'boards.cfg' @@ -202,6 +205,7 @@ class KconfigScanner: os.environ['KCONFIG_OBJDIR'] = '' self._tmpfile = None self._conf = kconfiglib.Kconfig(warn=False) + self._srctree = srctree def __del__(self): """Delete a leftover temporary file before exit. @@ -239,7 +243,26 @@ class KconfigScanner: expect_target, match, rear = leaf.partition('_defconfig') assert match and not rear, f'{leaf} : invalid defconfig' - self._conf.load_config(defconfig) + temp = None + if b'#include' in tools.read_file(defconfig): + cmd = [ + os.getenv('CPP', 'cpp'), + '-nostdinc', '-P', + '-I', self._srctree, + '-undef', + '-x', 'assembler-with-cpp', + defconfig] + result = command.run_pipe([cmd], capture=True, capture_stderr=True) + temp = tempfile.NamedTemporaryFile(prefix='buildman-') + tools.write_file(temp.name, result.stdout, False) + fname = temp.name + tout.info(f'Processing #include to produce {defconfig}') + else: + fname = defconfig + + self._conf.load_config(fname) + if temp: + del temp self._tmpfile = None params = {} diff --git a/tools/buildman/buildman.rst b/tools/buildman/buildman.rst index e873611e596..8acf236f2b3 100644 --- a/tools/buildman/buildman.rst +++ b/tools/buildman/buildman.rst @@ -1112,6 +1112,30 @@ The -U option uses the u-boot.env files which are produced by a build. Internally, buildman writes out an out-env file into the build directory for later comparison. +defconfig fragments +------------------- + +Buildman provides some initial support for configuration fragments. It can scan +these when present in defconfig files and handle the resuiting Kconfig +correctly. Thus it is possible to build a board which has a ``#include`` in the +defconfig file. + +For now, Buildman simply includes the files to produce a single output file, +using the C preprocessor. It does not call the ``merge_config.sh`` script. The +redefined/redundant logic in that script could fairly easily be repeated in +Buildman, to detect potential problems. For now it is not clear that this is +useful. + +To specify the C preprocessor to use, set the ``CPP`` environment variable. The +default is ``cpp``. + +Note that Buildman does not support adding fragments to existing boards, e.g. +like:: + + make qemu_riscv64_defconfig acpi.config + +This is partly because there is no way for Buildman to know which fragments are +valid on which boards. Building with clang ------------------- diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py index db3c9d63ec4..4e12c671a3d 100644 --- a/tools/buildman/func_test.py +++ b/tools/buildman/func_test.py @@ -2,8 +2,10 @@ # Copyright (c) 2014 Google, Inc # +import io import os from pathlib import Path +import re import shutil import sys import tempfile @@ -373,6 +375,22 @@ class TestFunctional(unittest.TestCase): def _HandleCommandSize(self, args): return command.CommandResult(return_code=0) + def _HandleCommandCpp(self, args): + # args ['-nostdinc', '-P', '-I', '/tmp/tmp7f17xk_o/src', '-undef', + # '-x', 'assembler-with-cpp', fname] + fname = args[7] + buf = io.StringIO() + for line in tools.read_file(fname, False).splitlines(): + if line.startswith('#include'): + # Example: #include <configs/renesas_rcar2.config> + m_incfname = re.match('#include <(.*)>', line) + data = tools.read_file(m_incfname.group(1), False) + for line in data.splitlines(): + print(line, file=buf) + else: + print(line, file=buf) + return command.CommandResult(stdout=buf.getvalue(), return_code=0) + def _HandleCommand(self, **kwargs): """Handle a command execution. @@ -406,6 +424,8 @@ class TestFunctional(unittest.TestCase): return self._HandleCommandObjcopy(args) elif cmd.endswith( 'size'): return self._HandleCommandSize(args) + elif cmd.endswith( 'cpp'): + return self._HandleCommandCpp(args) if not result: # Not handled, so abort @@ -1118,3 +1138,17 @@ CONFIG_SOC="fred" 'config': '-', 'target': 'board0'}, ['WARNING: board0_defconfig: No TARGET_BOARD0 enabled']), res) + + # check handling of #include files; see _HandleCommandCpp() + inc = os.path.join(src, 'common') + tools.write_file(inc, b'CONFIG_TARGET_BOARD0=y\n') + tools.write_file(norm, f'#include <{inc}>', False) + res = scanner.scan(norm, True) + self.assertEqual(({ + 'arch': 'arm', + 'cpu': 'armv7', + 'soc': '-', + 'vendor': 'Tester', + 'board': 'ARM Board 0', + 'config': 'config0', + 'target': 'board0'}, []), res) -- 2.34.1