Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add script for checking resulting kernel config #3006

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ jobs:
${{ needs.prepare.outputs.build_container_image }} \
make BUILDDIR=/build ${{ matrix.board.defconfig }}

- name: Check Linux config
run: |
docker run --rm --privileged -v "${GITHUB_WORKSPACE}:/build" \
-e BUILDER_UID="$(id -u)" -e BUILDER_GID="$(id -g)" \
-v "/mnt/cache:/cache" \
${{ needs.prepare.outputs.build_container_image }} \
make -C buildroot O="/build/output" BR2_EXTERNAL="/build/buildroot-external" \
BR2_CHECK_DOTCONFIG_OPTS="--github-format --strip-path-prefix=/build/" linux-check-dotconfig

- name: Upload artifacts
if: ${{ github.event_name != 'release' && needs.prepare.outputs.publish_build == 'true' }}
working-directory: output/images/
Expand Down
11 changes: 11 additions & 0 deletions buildroot-external/external.mk
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
include $(sort $(wildcard $(BR2_EXTERNAL_HASSOS_PATH)/package/*/*.mk))

.PHONY: linux-check-dotconfig
linux-check-dotconfig: linux-check-configuration-done
CC=$(TARGET_CC) LD=$(TARGET_LD) srctree=$(LINUX_SRCDIR) \
ARCH=$(if $(BR2_x86_64),x86,$(if $(BR2_arm)$(BR2_aarch64),arm,$(ARCH))) \
SRCARCH=$(if $(BR2_x86_64),x86,$(if $(BR2_arm)$(BR2_aarch64),arm,$(ARCH))) \
$(BR2_EXTERNAL_HASSOS_PATH)/scripts/check-dotconfig.py \
$(BR2_CHECK_DOTCONFIG_OPTS) \
--src-kconfig $(LINUX_SRCDIR)Kconfig \
--actual-config $(LINUX_SRCDIR).config \
$(shell echo $(BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES))
128 changes: 128 additions & 0 deletions buildroot-external/scripts/check-dotconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env python

import argparse
from collections import namedtuple
import re

from kconfiglib import Kconfig


# Can be either "CONFIG_OPTION=(y|m|n)" or "# CONFIG_OPTION is not set"
regex = re.compile(
r"^(CONFIG_(?P<option_set>[A-Z0-9_]+)=(?P<value>[mny])"
r"|# CONFIG_(?P<option_unset>[A-Z0-9_]+) is not set)$"
)

# use namedtuple as a lightweight representation of fragment-defined options
OptionValue = namedtuple("OptionValue", ["option", "value", "file", "line"])


def parse_fragment(
filename: str, strip_path_prefix: str = None
) -> dict[str, OptionValue]:
"""
Parse Buildroot Kconfig fragment and return dict of OptionValue objects.
"""
options: dict[str, OptionValue] = {}

with open(filename) as f:
if strip_path_prefix and filename.startswith(strip_path_prefix):
filename = filename[len(strip_path_prefix) :]

for line_number, line in enumerate(f, 1):
if matches := re.match(regex, line):
if matches["option_unset"]:
value = OptionValue(
matches["option_unset"], None, filename, line_number
)
options.update({matches.group("option_unset"): value})
else:
value = OptionValue(
matches["option_set"], matches["value"], filename, line_number
)
options.update({matches.group("option_set"): value})

return options


def _format_message(
message: str, file: str, line: int, github_format: bool = False
) -> str:
"""
Format message with source file and line number.
"""
if github_format:
return f"::warning file={file},line={line}::{message}"
return f"{message} (defined in {file}:{line})"


def compare_configs(
expected_options: dict[str, OptionValue],
kconfig: Kconfig,
github_format: bool = False,
) -> None:
"""
Compare dictionary of expected options with actual Kconfig representation.
"""
for option, spec in expected_options.items():
if option not in kconfig.syms:
print(
_format_message(
f"{option}={spec.value} not found",
file=spec.file,
line=spec.line,
github_format=github_format,
)
)
elif (val := kconfig.syms[option].str_value) != spec.value:
if spec.value is None and val == "n":
continue
print(
_format_message(
f"{option}={spec.value} requested, actual = {val}",
file=spec.file,
line=spec.line,
github_format=github_format,
)
)


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"--src-kconfig", help="Path to top-level Kconfig file", required=True
)
parser.add_argument(
"--actual-config",
help="Path to config with actual config values (.config)",
required=True,
)
parser.add_argument(
"--github-format",
action="store_true",
help="Use Github Workflow commands output format",
)
parser.add_argument(
"-s",
"--strip-path-prefix",
help="Path prefix to strip in the output from config fragment paths",
)
parser.add_argument("fragments", nargs="+", help="Paths to source config fragments")

args = parser.parse_args()

expected_options: dict[str, OptionValue] = {}

for f in args.fragments:
expected_options.update(
parse_fragment(f, strip_path_prefix=args.strip_path_prefix)
)

kconfig = Kconfig(args.src_kconfig, warn_to_stderr=False)
kconfig.load_config(args.actual_config)

compare_configs(expected_options, kconfig, github_format=args.github_format)


if __name__ == "__main__":
main()
Loading
Loading