This is an automated email from the ASF dual-hosted git repository.

simbit18 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 81d1126965c tools[feat]: add config check tool
81d1126965c is described below

commit 81d1126965c84a82552e06224fbf6711f1fd680c
Author: xuxin19 <[email protected]>
AuthorDate: Mon Mar 3 22:05:27 2025 +0800

    tools[feat]: add config check tool
    
    [checkkconfig.py] is a tool that simulates the effects of modifying a 
CONFIG item,
    Can be used to check whether my config changes are what I expected.
    
    Signed-off-by: xuxin19 <[email protected]>
---
 Documentation/components/tools/index.rst |  42 +++++
 tools/checkkconfig.py                    | 307 +++++++++++++++++++++++++++++++
 2 files changed, 349 insertions(+)

diff --git a/Documentation/components/tools/index.rst 
b/Documentation/components/tools/index.rst
index f380acfbe20..65ccb8e0545 100644
--- a/Documentation/components/tools/index.rst
+++ b/Documentation/components/tools/index.rst
@@ -13,6 +13,48 @@ cmpconfig.c
 This C file can be used to build a utility for comparing two NuttX
 configuration files.
 
+checkkconfig.py
+---------------
+
+``checkkconfig.py`` is a Python script that simulates the effects of modifying 
a CONFIG item.
+It can be used to check whether my config changes are what I expected.
+
+Help message::
+
+  $ tools/checkkconfig.py -h
+  usage: checkkconfig.py [-h] -f FILE (-s CONFIG VALUE | -d DIFF)
+
+  optional arguments:
+    -h, --help            show this help message and exit
+    -f FILE, --file FILE  Path to the input defconfig file
+    -s CONFIG_XXX VALUE, --single CONFIG VALUE
+                          Analyze single change: CONFIG_NAME y/m/n
+    -d DIFF, --diff DIFF  Analyze changes from diff file
+
+  example: ./tools/checkkconfig.py -f defconfig -s ELF n
+
+  outputs:
+  Change report for ELF=n
+  Config Option                            Old                  New
+  ----------------------------------------------------------------------
+  BINFMT_LOADABLE                          y                    n
+  ELF                                      y                    n
+  ELF_STACKSIZE                            8192                 <unset>
+  LIBC_ARCH_ELF                            y                    n
+  LIBC_MODLIB                              y                    n
+  MODLIB_ALIGN_LOG2                        2                    <unset>
+  MODLIB_BUFFERINCR                        32                   <unset>
+  MODLIB_BUFFERSIZE                        32                   <unset>
+  MODLIB_MAXDEPEND                         2                    <unset>
+  MODLIB_RELOCATION_BUFFERCOUNT            256                  <unset>
+  MODLIB_SYMBOL_CACHECOUNT                 256                  <unset>
+
+As we can see, we can clearly know that
+if I turn off ELF in defconfig at this time,
+it will bring about the following configuration linkage changes
+
+It can also parse diff files, which can be used to check the changes of 
multiple configs.
+
 checkpatch.sh
 -------------
 
diff --git a/tools/checkkconfig.py b/tools/checkkconfig.py
new file mode 100755
index 00000000000..74c5c0415c4
--- /dev/null
+++ b/tools/checkkconfig.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+# tools/checkkconfig.py
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# [checkkconfig.py] is a tool that simulates the effects of modifying a CONFIG 
item,
+# Can be used to check whether my config changes are what I expected.
+#
+# usage: checkkconfig.py [-h] -f FILE (-s CONFIG VALUE | -d DIFF)
+#
+# optional arguments:
+#  -h, --help            show this help message and exit
+#  -f FILE, --file FILE  Path to the input defconfig file
+#  -s CONFIG_XXX VALUE, --single CONFIG VALUE
+#                        Analyze single change: CONFIG_NAME y/m/n
+#  -d DIFF, --diff DIFF  Analyze changes from diff file
+#
+# example: ./tools/checkkconfig.py -f defconfig -s ELF n
+#
+# outputs:
+# Change report for ELF=n
+# Config Option                            Old                  New
+# ----------------------------------------------------------------------
+# BINFMT_LOADABLE                          y                    n
+# ELF                                      y                    n
+# ELF_STACKSIZE                            8192                 <unset>
+# LIBC_ARCH_ELF                            y                    n
+# LIBC_MODLIB                              y                    n
+# MODLIB_ALIGN_LOG2                        2                    <unset>
+# MODLIB_BUFFERINCR                        32                   <unset>
+# MODLIB_BUFFERSIZE                        32                   <unset>
+# MODLIB_MAXDEPEND                         2                    <unset>
+# MODLIB_RELOCATION_BUFFERCOUNT            256                  <unset>
+# MODLIB_SYMBOL_CACHECOUNT                 256                  <unset>
+#
+# As we can see, we can clearly know that
+# if I turn off ELF in defconfig at this time,
+# it will bring about the following configuration linkage changes
+#
+# It can also parse diff files, which can be used to check the changes of 
multiple configs.
+# diff file example:
+#  diff --git a/boards/demo/configs/nsh/defconfig 
b/boards/demo/configs/nsh/defconfig
+#  index cf7d07c..5de20d4 100644
+#  --- a/boards/demo/configs/nsh/defconfig
+#  +++ b/boards/demo/configs/nsh/defconfig
+#  @@ -51,7 +51,6 @@ CONFIG_ARMV7A_STRING_FUNCTION=y
+#   CONFIG_ARM_PSCI=y
+#   CONFIG_ARM_SEMIHOSTING_HOSTFS=y
+#   CONFIG_ARM_THUMB=y
+#  -CONFIG_AUDIO=y
+#   CONFIG_BCH=y
+#   CONFIG_BINFMT_ELF_EXECUTABLE=y
+#   CONFIG_BLUETOOTH=y
+#  @@ -104,7 +103,6 @@ CONFIG_DRIVERS_VIRTIO_SERIAL=y
+#   CONFIG_DRIVERS_VIRTIO_SOUND=y
+#   CONFIG_DRIVERS_WIFI_SIM=y
+#   CONFIG_DRIVERS_WIRELESS=y
+#  -CONFIG_ELF=y
+#   CONFIG_ETC_ROMFS=y
+#   CONFIG_EVENT_FD=y
+#   CONFIG_EXAMPLES_FB=y
+#
+# example: ./tools/checkkconfig.py -f defconfig -d changes.diff
+#
+# outputs:
+# Change report for diff: changes.diff
+# Config Option                            Old                  New
+# ----------------------------------------------------------------------
+# AUDIO                                    y                    n
+# AUDIO_BUFFER_NUMBYTES                    8192                 <unset>
+# AUDIO_EXCLUDE_EQUALIZER                  y                    n
+# AUDIO_EXCLUDE_REWIND                     y                    n
+# AUDIO_FORMAT_AMR                         y                    n
+# AUDIO_FORMAT_MP3                         y                    n
+# AUDIO_FORMAT_OPUS                        y                    n
+# AUDIO_FORMAT_PCM                         y                    n
+# AUDIO_FORMAT_SBC                         y                    n
+# AUDIO_NUM_BUFFERS                        2                    <unset>
+# BINFMT_LOADABLE                          y                    n
+# ELF                                      y                    n
+# ELF_STACKSIZE                            8192                 <unset>
+# LIBC_ARCH_ELF                            y                    n
+# LIBC_MODLIB                              y                    n
+# MODLIB_ALIGN_LOG2                        2                    <unset>
+# MODLIB_BUFFERINCR                        32                   <unset>
+# MODLIB_BUFFERSIZE                        32                   <unset>
+# MODLIB_MAXDEPEND                         2                    <unset>
+# MODLIB_RELOCATION_BUFFERCOUNT            256                  <unset>
+# MODLIB_SYMBOL_CACHECOUNT                 256                  <unset>
+# NXPLAYER_COMMAND_LINE                    y                    n
+# NXPLAYER_DEFAULT_MEDIADIR                /music               <unset>
+# NXPLAYER_FMT_FROM_EXT                    y                    n
+# NXPLAYER_INCLUDE_DEVICE_SEARCH           y                    n
+# NXPLAYER_INCLUDE_HELP                    y                    n
+# NXPLAYER_INCLUDE_MEDIADIR                y                    n
+# NXPLAYER_INCLUDE_PREFERRED_DEVICE        y                    n
+# NXPLAYER_MAINTHREAD_STACKSIZE            8192                 <unset>
+# NXPLAYER_PLAYTHREAD_STACKSIZE            8192                 <unset>
+# NXRECORDER_COMMAND_LINE                  y                    n
+# NXRECORDER_FMT_FROM_EXT                  y                    n
+# NXRECORDER_INCLUDE_HELP                  y                    n
+# NXRECORDER_MAINTHREAD_STACKSIZE          8192                 <unset>
+# NXRECORDER_RECORDTHREAD_STACKSIZE        8192                 <unset>
+# SYSTEM_NXPLAYER                          y                    n
+# SYSTEM_NXRECORDER                        y                    n
+#
+#
+# RECAUTION:
+# Because NuttX apps Kconfig of menu is generated by build system,
+# and arch/board bridge Kconfig is symlink to real arch board dir.
+# So it is best to check the defconfig that has been configured.
+# If the environment does not generate Kconfig menu, etc.
+# the tool will execute `configure.sh` and distclean at the end.
+#
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+from pathlib import Path
+
+try:
+    from kconfiglib import Kconfig
+except ImportError:
+    print(
+        "ERROR: checkkconfig tool depends on kconfiglib for parse Kconfig 
tree.\nplease install it.\npip install kconfiglib",
+        file=sys.stderr,
+    )
+    sys.exit(1)
+
+TOPDIR = Path(__file__).resolve().parent.parent
+DPATH = Path("defconfig")
+NEED_RESET = False
+
+
+# Prepare environment for Kconfig
+def prepare_env():
+    global NEED_RESET
+    # check if we are in the configured Kconfig environment
+    full_config_file = TOPDIR / Path(".config")
+    if not full_config_file.exists():
+        print("apps preconfig do not generate yet \nrun configure.sh first")
+        result = subprocess.run(
+            [f"{TOPDIR}/tools/configure.sh", "-e", f"{str(DPATH.parent)}"],
+            capture_output=True,
+            text=True,
+        )
+        if result.returncode != 0:
+            print(
+                f"ERROR: {TOPDIR}/tools/configure.sh run fail\n configure 
path: {str(DPATH.parent)}",
+                file=sys.stderr,
+            )
+            print(f"STDERROR:{result.stderr}", file=sys.stderr)
+            sys.exit(1)
+        NEED_RESET = True
+    os.environ["APPSDIR"] = "../apps"
+    os.environ["APPSBINDIR"] = "../apps"
+    os.environ["EXTERNALDIR"] = "dummy"
+    os.environ["BINDIR"] = str(TOPDIR)
+    os.environ["KCONFIG_CONFIG"] = str(DPATH)
+
+
+# Reset environment to previous
+def reset_env():
+    os.environ.pop("APPSDIR", None)
+    os.environ.pop("APPSBINDIR", None)
+    os.environ.pop("EXTERNALDIR", None)
+    os.environ.pop("BINDIR", None)
+    os.environ.pop("KCONFIG_CONFIG", None)
+    if NEED_RESET:
+        result = subprocess.run(
+            ["make", "distclean"], cwd=TOPDIR, capture_output=True, text=True
+        )
+        print(result.stdout)
+        if result.returncode != 0:
+            print(
+                "ERROR: distclean error please clean up your workspace 
manually",
+                file=sys.stderr,
+            )
+            print(f"STDERROR:{result.stderr}", file=sys.stderr)
+
+
+# Parse a diff file and return a dict of changes
+def parse_diff(diff_path):
+    changes = {}
+    diff_pattern = re.compile(r"^([+-])CONFIG_(\w+)=([ymn])$")
+    with open(diff_path, "r") as f:
+        for line in f:
+            line = line.strip()
+
+            match = diff_pattern.match(line)
+            if not match:
+                continue
+
+            op, name, value = match.groups()
+            full_name = f"{name}"
+
+            if op == "-":
+                changes[full_name] = "n"
+            elif op == "+":
+                changes[full_name] = value.lower()
+    return changes
+
+
+# Apply a set of changes to a Kconfig tree and return a list of changed symbols
+def apply_changes(kconf, changes):
+
+    # step 1: keep track of original values
+    orig_state = {sym.name: sym.str_value for sym in kconf.defined_syms}
+
+    # step 2: apply the config settings
+    value_map = {"n": 0, "m": 1, "y": 2}
+    for target, value in changes.items():
+        sym = kconf.syms.get(target)
+        if not sym:
+            print(f"Warning: {target} not found, skipped")
+            continue
+        if value not in value_map:
+            print(f"Invalid value {value} for {target}, skipped")
+            continue
+        sym.set_value(value_map[value])
+
+    # step 3: check for changes
+    changed = []
+    for sym in kconf.defined_syms:
+        orig = orig_state.get(sym.name, "")
+        curr = sym.str_value
+        if orig != curr:
+            changed.append((sym.name, orig, curr))
+
+    return changed
+
+
+def track_single_change(target, value):
+    kconf = Kconfig()
+    kconf.load_config()
+    return apply_changes(kconf, {target: value})
+
+
+def track_diff_changes(diff_path):
+    kconf = Kconfig()
+    kconf.load_config()
+    changes = parse_diff(diff_path)
+    return apply_changes(kconf, changes)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "-f", "--file", required=True, help="Path to the input defconfig file"
+    )
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument(
+        "-s",
+        "--single",
+        nargs=2,
+        metavar=("CONFIG", "VALUE"),
+        help="Analyze single change: CONFIG_NAME y/m/n",
+    )
+    group.add_argument("-d", "--diff", help="Analyze changes from diff file")
+
+    args = parser.parse_args()
+
+    DPATH = Path(args.file)
+
+    if not DPATH.is_absolute():
+        DPATH = TOPDIR / DPATH
+
+    if not DPATH.exists:
+        print("ERROR: defconfig file DO NOT exist", file=sys.stderr)
+        sys.exit(1)
+
+    prepare_env()
+
+    if args.single:
+        target, value = args.single
+        changes = track_single_change(target, value.lower())
+        title = f"Change report for {target}={value}"
+    elif args.diff:
+        changes = track_diff_changes(args.diff)
+        title = f"Change report for diff: {args.diff}"
+
+    reset_env()
+
+    print(f"\n{title}")
+    print(f"{'Config Option':<40} {'Old':<20} {'New':<20}")
+    print("-" * 70)
+    for name, old, new in sorted(changes, key=lambda x: x[0]):
+        print(f"{name:<40} {old or '<unset>':<20} {new or '<unset>':<20}")

Reply via email to