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 <grzegorz.swiderski@nordicsemi.no>
This commit is contained in:
parent
37bce60188
commit
7ce6ac225e
3 changed files with 72 additions and 7 deletions
2
.github/workflows/compliance.yml
vendored
2
.github/workflows/compliance.yml
vendored
|
@ -82,7 +82,7 @@ jobs:
|
||||||
git log --pretty=oneline | head -n 10
|
git log --pretty=oneline | head -n 10
|
||||||
# Increase rename limit to allow for large PRs
|
# Increase rename limit to allow for large PRs
|
||||||
git config diff.renameLimit 10000
|
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}..
|
-c origin/${BASE_REF}..
|
||||||
|
|
||||||
- name: upload-results
|
- name: upload-results
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -104,5 +104,8 @@ Nits.txt
|
||||||
Pylint.txt
|
Pylint.txt
|
||||||
Ruff.txt
|
Ruff.txt
|
||||||
SphinxLint.txt
|
SphinxLint.txt
|
||||||
|
SysbuildKconfig.txt
|
||||||
|
SysbuildKconfigBasic.txt
|
||||||
|
SysbuildKconfigBasicNoModules.txt
|
||||||
TextEncoding.txt
|
TextEncoding.txt
|
||||||
YAMLLint.txt
|
YAMLLint.txt
|
||||||
|
|
|
@ -374,6 +374,9 @@ class KconfigCheck(ComplianceTest):
|
||||||
# Top-level Kconfig file. The path can be relative to srctree (ZEPHYR_BASE).
|
# Top-level Kconfig file. The path can be relative to srctree (ZEPHYR_BASE).
|
||||||
FILENAME = "Kconfig"
|
FILENAME = "Kconfig"
|
||||||
|
|
||||||
|
# Kconfig symbol prefix/namespace.
|
||||||
|
CONFIG_ = "CONFIG_"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
kconf = self.parse_kconfig()
|
kconf = self.parse_kconfig()
|
||||||
|
|
||||||
|
@ -385,7 +388,7 @@ class KconfigCheck(ComplianceTest):
|
||||||
self.check_soc_name_sync(kconf)
|
self.check_soc_name_sync(kconf)
|
||||||
self.check_no_undef_outside_kconfig(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
|
Get a list of modules and put them in a file that is parsed by
|
||||||
Kconfig
|
Kconfig
|
||||||
|
@ -398,7 +401,9 @@ class KconfigCheck(ComplianceTest):
|
||||||
zephyr_module_path = os.path.join(ZEPHYR_BASE, "scripts",
|
zephyr_module_path = os.path.join(ZEPHYR_BASE, "scripts",
|
||||||
"zephyr_module.py")
|
"zephyr_module.py")
|
||||||
cmd = [sys.executable, zephyr_module_path,
|
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:
|
try:
|
||||||
subprocess.run(cmd, check=True, stdout=subprocess.PIPE,
|
subprocess.run(cmd, check=True, stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
|
@ -485,6 +490,7 @@ class KconfigCheck(ComplianceTest):
|
||||||
|
|
||||||
kconfig_file = os.path.join(kconfig_dir, 'boards', 'Kconfig')
|
kconfig_file = os.path.join(kconfig_dir, 'boards', 'Kconfig')
|
||||||
kconfig_boards_file = os.path.join(kconfig_dir, 'boards', 'Kconfig.boards')
|
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')
|
kconfig_defconfig_file = os.path.join(kconfig_dir, 'boards', 'Kconfig.defconfig')
|
||||||
|
|
||||||
board_roots = self.get_module_setting_root('board', settings_file)
|
board_roots = self.get_module_setting_root('board', settings_file)
|
||||||
|
@ -501,6 +507,11 @@ class KconfigCheck(ComplianceTest):
|
||||||
for board_dir in board.directories:
|
for board_dir in board.directories:
|
||||||
fp.write('osource "' + (board_dir / 'Kconfig.defconfig').as_posix() + '"\n')
|
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:
|
with open(kconfig_boards_file, 'w') as fp:
|
||||||
for board in v2_boards:
|
for board in v2_boards:
|
||||||
board_str = 'BOARD_' + re.sub(r"[^a-zA-Z0-9_]", "_", board.name).upper()
|
board_str = 'BOARD_' + re.sub(r"[^a-zA-Z0-9_]", "_", board.name).upper()
|
||||||
|
@ -522,6 +533,7 @@ class KconfigCheck(ComplianceTest):
|
||||||
fp.write('osource "' + (board_dir / 'Kconfig').as_posix() + '"\n')
|
fp.write('osource "' + (board_dir / 'Kconfig').as_posix() + '"\n')
|
||||||
|
|
||||||
kconfig_defconfig_file = os.path.join(kconfig_dir, 'soc', 'Kconfig.defconfig')
|
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_soc_file = os.path.join(kconfig_dir, 'soc', 'Kconfig.soc')
|
||||||
kconfig_file = os.path.join(kconfig_dir, 'soc', 'Kconfig')
|
kconfig_file = os.path.join(kconfig_dir, 'soc', 'Kconfig')
|
||||||
|
|
||||||
|
@ -533,6 +545,10 @@ class KconfigCheck(ComplianceTest):
|
||||||
for folder in soc_folders:
|
for folder in soc_folders:
|
||||||
fp.write('osource "' + (Path(folder) / 'Kconfig.defconfig').as_posix() + '"\n')
|
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:
|
with open(kconfig_soc_file, 'w') as fp:
|
||||||
for folder in soc_folders:
|
for folder in soc_folders:
|
||||||
fp.write('source "' + (Path(folder) / 'Kconfig.soc').as_posix() + '"\n')
|
fp.write('source "' + (Path(folder) / 'Kconfig.soc').as_posix() + '"\n')
|
||||||
|
@ -587,6 +603,7 @@ class KconfigCheck(ComplianceTest):
|
||||||
|
|
||||||
# For multi repo support
|
# For multi repo support
|
||||||
self.get_modules(os.path.join(kconfiglib_dir, "Kconfig.modules"),
|
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"))
|
os.path.join(kconfiglib_dir, "settings_file.txt"))
|
||||||
# For Kconfig.dts support
|
# For Kconfig.dts support
|
||||||
self.get_kconfig_dts(os.path.join(kconfiglib_dir, "Kconfig.dts"),
|
self.get_kconfig_dts(os.path.join(kconfiglib_dir, "Kconfig.dts"),
|
||||||
|
@ -833,7 +850,7 @@ Missing SoC names or CONFIG_SOC vs soc.yml out of sync:
|
||||||
undef_to_locs = collections.defaultdict(list)
|
undef_to_locs = collections.defaultdict(list)
|
||||||
|
|
||||||
# Warning: Needs to work with both --perl-regexp and the 're' module
|
# 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
|
# Skip doc/releases and doc/security/vulnerabilities.rst, which often
|
||||||
# reference removed symbols
|
# reference removed symbols
|
||||||
|
@ -849,7 +866,7 @@ Missing SoC names or CONFIG_SOC vs soc.yml out of sync:
|
||||||
# Extract symbol references (might be more than one) within the
|
# Extract symbol references (might be more than one) within the
|
||||||
# line
|
# line
|
||||||
for sym_name in re.findall(regex, 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 \
|
if sym_name not in defined_syms and \
|
||||||
sym_name not in self.UNDEF_KCONFIG_ALLOWLIST and \
|
sym_name not in self.UNDEF_KCONFIG_ALLOWLIST and \
|
||||||
not (sym_name.endswith("_MODULE") and sym_name[:-7] in defined_syms):
|
not (sym_name.endswith("_MODULE") and sym_name[:-7] in defined_syms):
|
||||||
|
@ -865,7 +882,7 @@ Missing SoC names or CONFIG_SOC vs soc.yml out of sync:
|
||||||
#
|
#
|
||||||
# CONFIG_ALSO_MISSING arch/xtensa/core/fatal.c:273
|
# CONFIG_ALSO_MISSING arch/xtensa/core/fatal.c:273
|
||||||
# CONFIG_MISSING arch/xtensa/core/fatal.c:264, subsys/fb/cfb.c:20
|
# 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()))
|
for sym_name, locs in sorted(undef_to_locs.items()))
|
||||||
|
|
||||||
self.failure(f"""
|
self.failure(f"""
|
||||||
|
@ -1031,10 +1048,13 @@ class KconfigBasicNoModulesCheck(KconfigBasicCheck):
|
||||||
"""
|
"""
|
||||||
name = "KconfigBasicNoModules"
|
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:
|
with open(modules_file, 'w') as fp_module_file:
|
||||||
fp_module_file.write("# Empty\n")
|
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):
|
class KconfigHWMv2Check(KconfigBasicCheck):
|
||||||
"""
|
"""
|
||||||
|
@ -1050,6 +1070,48 @@ class KconfigHWMv2Check(KconfigBasicCheck):
|
||||||
FILENAME = os.path.join(os.path.dirname(__file__), "Kconfig.board.v2")
|
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):
|
class Nits(ComplianceTest):
|
||||||
"""
|
"""
|
||||||
Checks various nits in added/modified files. Doesn't check stuff that's
|
Checks various nits in added/modified files. Doesn't check stuff that's
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue