From 7ce6ac225e3d8deaa956075a4e131f89c37daabc Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Mon, 13 Jan 2025 08:31:40 +0100 Subject: [PATCH] scripts: ci: check_compliance: Add sysbuild Kconfig checks Introduce sysbuild-specific variants of existing Kconfig checks: * SysbuildKconfig * SysbuildKconfigBasic * SysbuildKconfigBasicNoModules This involves a few additions to the base `KconfigCheck` class: * Supporting a variable symbol prefix, to handle `SB_CONFIG_`. * Generating extra files, including `Kconfig.sysbuild.modules`. Although these are never sourced outside of sysbuild Kconfig, they're still generated for every regular Zephyr build, so it's natural to let all Kconfig checks follow this behavior. Signed-off-by: Grzegorz Swiderski --- .github/workflows/compliance.yml | 2 +- .gitignore | 3 ++ scripts/ci/check_compliance.py | 74 +++++++++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml index 2ad48848fedc..1875de7cb7e8 100644 --- a/.github/workflows/compliance.yml +++ b/.github/workflows/compliance.yml @@ -82,7 +82,7 @@ jobs: git log --pretty=oneline | head -n 10 # Increase rename limit to allow for large PRs git config diff.renameLimit 10000 - ./scripts/ci/check_compliance.py --annotate -e KconfigBasic -e ClangFormat \ + ./scripts/ci/check_compliance.py --annotate -e KconfigBasic -e SysbuildKconfigBasic -e ClangFormat \ -c origin/${BASE_REF}.. - name: upload-results diff --git a/.gitignore b/.gitignore index b545443db310..aab3981f52fc 100644 --- a/.gitignore +++ b/.gitignore @@ -104,5 +104,8 @@ Nits.txt Pylint.txt Ruff.txt SphinxLint.txt +SysbuildKconfig.txt +SysbuildKconfigBasic.txt +SysbuildKconfigBasicNoModules.txt TextEncoding.txt YAMLLint.txt diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index c2709b88b72a..c096cb5f4cac 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -374,6 +374,9 @@ class KconfigCheck(ComplianceTest): # Top-level Kconfig file. The path can be relative to srctree (ZEPHYR_BASE). FILENAME = "Kconfig" + # Kconfig symbol prefix/namespace. + CONFIG_ = "CONFIG_" + def run(self): kconf = self.parse_kconfig() @@ -385,7 +388,7 @@ def run(self): self.check_soc_name_sync(kconf) self.check_no_undef_outside_kconfig(kconf) - def get_modules(self, modules_file, settings_file): + def get_modules(self, modules_file, sysbuild_modules_file, settings_file): """ Get a list of modules and put them in a file that is parsed by Kconfig @@ -398,7 +401,9 @@ def get_modules(self, modules_file, settings_file): zephyr_module_path = os.path.join(ZEPHYR_BASE, "scripts", "zephyr_module.py") cmd = [sys.executable, zephyr_module_path, - '--kconfig-out', modules_file, '--settings-out', settings_file] + '--kconfig-out', modules_file, + '--sysbuild-kconfig-out', sysbuild_modules_file, + '--settings-out', settings_file] try: subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -485,6 +490,7 @@ def get_v2_model(self, kconfig_dir, settings_file): kconfig_file = os.path.join(kconfig_dir, 'boards', 'Kconfig') kconfig_boards_file = os.path.join(kconfig_dir, 'boards', 'Kconfig.boards') + kconfig_sysbuild_file = os.path.join(kconfig_dir, 'boards', 'Kconfig.sysbuild') kconfig_defconfig_file = os.path.join(kconfig_dir, 'boards', 'Kconfig.defconfig') board_roots = self.get_module_setting_root('board', settings_file) @@ -501,6 +507,11 @@ def get_v2_model(self, kconfig_dir, settings_file): for board_dir in board.directories: fp.write('osource "' + (board_dir / 'Kconfig.defconfig').as_posix() + '"\n') + with open(kconfig_sysbuild_file, 'w') as fp: + for board in v2_boards: + for board_dir in board.directories: + fp.write('osource "' + (board_dir / 'Kconfig.sysbuild').as_posix() + '"\n') + with open(kconfig_boards_file, 'w') as fp: for board in v2_boards: board_str = 'BOARD_' + re.sub(r"[^a-zA-Z0-9_]", "_", board.name).upper() @@ -522,6 +533,7 @@ def get_v2_model(self, kconfig_dir, settings_file): fp.write('osource "' + (board_dir / 'Kconfig').as_posix() + '"\n') kconfig_defconfig_file = os.path.join(kconfig_dir, 'soc', 'Kconfig.defconfig') + kconfig_sysbuild_file = os.path.join(kconfig_dir, 'soc', 'Kconfig.sysbuild') kconfig_soc_file = os.path.join(kconfig_dir, 'soc', 'Kconfig.soc') kconfig_file = os.path.join(kconfig_dir, 'soc', 'Kconfig') @@ -533,6 +545,10 @@ def get_v2_model(self, kconfig_dir, settings_file): for folder in soc_folders: fp.write('osource "' + (Path(folder) / 'Kconfig.defconfig').as_posix() + '"\n') + with open(kconfig_sysbuild_file, 'w') as fp: + for folder in soc_folders: + fp.write('osource "' + (Path(folder) / 'Kconfig.sysbuild').as_posix() + '"\n') + with open(kconfig_soc_file, 'w') as fp: for folder in soc_folders: fp.write('source "' + (Path(folder) / 'Kconfig.soc').as_posix() + '"\n') @@ -587,6 +603,7 @@ def parse_kconfig(self): # For multi repo support self.get_modules(os.path.join(kconfiglib_dir, "Kconfig.modules"), + os.path.join(kconfiglib_dir, "Kconfig.sysbuild.modules"), os.path.join(kconfiglib_dir, "settings_file.txt")) # For Kconfig.dts support self.get_kconfig_dts(os.path.join(kconfiglib_dir, "Kconfig.dts"), @@ -833,7 +850,7 @@ def check_no_undef_outside_kconfig(self, kconf): undef_to_locs = collections.defaultdict(list) # Warning: Needs to work with both --perl-regexp and the 're' module - regex = r"\bCONFIG_[A-Z0-9_]+\b(?!\s*##|[$@{(.*])" + regex = r"\b" + self.CONFIG_ + r"[A-Z0-9_]+\b(?!\s*##|[$@{(.*])" # Skip doc/releases and doc/security/vulnerabilities.rst, which often # reference removed symbols @@ -849,7 +866,7 @@ def check_no_undef_outside_kconfig(self, kconf): # Extract symbol references (might be more than one) within the # line for sym_name in re.findall(regex, line): - sym_name = sym_name[7:] # Strip CONFIG_ + sym_name = sym_name[len(self.CONFIG_):] # Strip CONFIG_ if sym_name not in defined_syms and \ sym_name not in self.UNDEF_KCONFIG_ALLOWLIST and \ not (sym_name.endswith("_MODULE") and sym_name[:-7] in defined_syms): @@ -865,7 +882,7 @@ def check_no_undef_outside_kconfig(self, kconf): # # CONFIG_ALSO_MISSING arch/xtensa/core/fatal.c:273 # CONFIG_MISSING arch/xtensa/core/fatal.c:264, subsys/fb/cfb.c:20 - undef_desc = "\n".join(f"CONFIG_{sym_name:35} {', '.join(locs)}" + undef_desc = "\n".join(f"{self.CONFIG_}{sym_name:35} {', '.join(locs)}" for sym_name, locs in sorted(undef_to_locs.items())) self.failure(f""" @@ -1031,10 +1048,13 @@ class KconfigBasicNoModulesCheck(KconfigBasicCheck): """ name = "KconfigBasicNoModules" - def get_modules(self, modules_file, settings_file): + def get_modules(self, modules_file, sysbuild_modules_file, settings_file): with open(modules_file, 'w') as fp_module_file: fp_module_file.write("# Empty\n") + with open(sysbuild_modules_file, 'w') as fp_module_file: + fp_module_file.write("# Empty\n") + class KconfigHWMv2Check(KconfigBasicCheck): """ @@ -1050,6 +1070,48 @@ class KconfigHWMv2Check(KconfigBasicCheck): FILENAME = os.path.join(os.path.dirname(__file__), "Kconfig.board.v2") +class SysbuildKconfigCheck(KconfigCheck): + """ + Checks if we are introducing any new warnings/errors with sysbuild Kconfig, + for example using undefined Kconfig variables. + """ + name = "SysbuildKconfig" + + FILENAME = "share/sysbuild/Kconfig" + CONFIG_ = "SB_CONFIG_" + + # A different allowlist is used for symbols prefixed with SB_CONFIG_ (omitted here). + UNDEF_KCONFIG_ALLOWLIST = { + # zephyr-keep-sorted-start re(^\s+") + "FOO", + "SECOND_SAMPLE", # Used in sysbuild documentation + "SUIT_ENVELOPE", # Used by nRF runners to program provisioning data + "SUIT_MPI_APP_AREA_PATH", # Used by nRF runners to program provisioning data + "SUIT_MPI_GENERATE", # Used by nRF runners to program provisioning data + "SUIT_MPI_RAD_AREA_PATH", # Used by nRF runners to program provisioning data + # zephyr-keep-sorted-stop + } + + +class SysbuildKconfigBasicCheck(SysbuildKconfigCheck, KconfigBasicCheck): + """ + Checks if we are introducing any new warnings/errors with sysbuild Kconfig, + for example using undefined Kconfig variables. + This runs the basic Kconfig test, which is checking only for undefined + references inside the sysbuild Kconfig tree. + """ + name = "SysbuildKconfigBasic" + + +class SysbuildKconfigBasicNoModulesCheck(SysbuildKconfigCheck, KconfigBasicNoModulesCheck): + """ + Checks if we are introducing any new warnings/errors with sysbuild Kconfig + when no modules are available. Catches symbols used in the main repository + but defined only in a module. + """ + name = "SysbuildKconfigBasicNoModules" + + class Nits(ComplianceTest): """ Checks various nits in added/modified files. Doesn't check stuff that's