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

xiaoxiang781216 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nuttx-ntfc.git

commit a5f250ebaedde27c289500c61951b53a3eebf037
Author: raiden00pl <[email protected]>
AuthorDate: Tue Aug 4 20:51:08 2026 +0200

    builder.py: reconfigure after applying Kconfig overrides
    
    CMake registers application targets at configure time from .config, but
    kv overrides are applied after configuring, so an override enabling a
    new application changed .config without ever creating its build target:
    code-level options took effect through the .config -> config.h rule
    while the application never appeared in the image. Overrides can also
    unlock suboptions (e.g. *_PROGNAME) whose missing defaults make cmake
    drop the application silently. Run olddefconfig and configure again
    after applying overrides.
    
    Signed-off-by: raiden00pl <[email protected]>
    Assisted-by: Claude Code
---
 src/ntfc/builder.py   | 22 +++++++++++++++-------
 tests/test_builder.py | 52 ++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/src/ntfc/builder.py b/src/ntfc/builder.py
index f99820c..22d10b1 100644
--- a/src/ntfc/builder.py
+++ b/src/ntfc/builder.py
@@ -24,6 +24,7 @@ import os
 import re
 import shutil
 import subprocess
+from functools import partial
 from pathlib import Path
 from typing import Any, Dict, List, Optional
 
@@ -351,7 +352,6 @@ class NuttXBuilder:
             "--build",
             str(build_path),
         ]
-
         run_env = os.environ.copy()
         if env:
             run_env.update(env)  # pragma: no cover
@@ -431,8 +431,8 @@ class NuttXBuilder:
             if not already_build or self._rebuild:  # pragma: no cover
                 self._log_kconfig_overrides(kv_overrides)
 
-                # configure build
-                self._run_cmake(
+                configure = partial(
+                    self._run_cmake,
                     source=nuttx_dir,
                     build=build_path,
                     generator="Ninja",
@@ -440,20 +440,28 @@ class NuttXBuilder:
                     env=build_env,
                 )
 
+                # configure build
+                configure()
+
                 # apply Kconfig overrides to generated .config before build
                 self._apply_kconfig_overrides(
                     nuttx_conf_path, kv_overrides, cfg_cwd
                 )
 
-                # Regenerate include/nuttx/config.h after changing .config.
-                # Otherwise CMake can relink an image built with stale Kconfig
-                # values while the saved .config claims the override applied.
-
                 if kv_overrides:
+                    # fill in defaults of suboptions the overrides
+                    # unlocked (e.g. *_PROGNAME): applications are
+                    # silently dropped at configure when these are
+                    # missing from .config
                     self._run_build_target(
                         build_path, "olddefconfig", env=build_env
                     )
 
+                    # application targets are registered at configure
+                    # time from .config: configure again so overrides
+                    # that enable new applications take effect
+                    configure()
+
                 # build
                 self._run_build(build_path, env=build_env)
 
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 1dcb0fe..cd5f7a0 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -143,6 +143,35 @@ def test_builder_flat_mode_no_kernel_keys(tmp_path) -> 
None:
     )
 
 
+def test_builder_reconfigures_after_kv_overrides(monkeypatch) -> None:
+    config = copy.deepcopy(conf_dir)
+    config["product"]["cores"]["core0"]["defconfig"] = "dummy/path"
+    config["product"]["cores"]["core0"]["kv"] = {"CONFIG_SYSTEM_X": "y"}
+
+    calls = []
+
+    def run_command_capture(cmd, env):
+        calls.append(cmd)
+
+    b = NuttXBuilder(config)
+    b._run_command = run_command_capture
+    b._make_dir = builder_make_dir_dummy
+    monkeypatch.setattr(
+        b, "_apply_kconfig_overrides", lambda *args, **kwargs: None
+    )
+
+    b.build_all()
+
+    # application targets are registered at configure time: overrides
+    # require an olddefconfig (defaults of unlocked suboptions) and a
+    # second configure before the build
+    assert [cmd[0] for cmd in calls] == ["cmake"] * 4
+    assert calls[1][-1] == "olddefconfig"
+    assert calls[0][:2] == calls[2][:2]
+    assert calls[3][:2] == ["cmake", "--build"]
+    assert "olddefconfig" not in calls[3]
+
+
 def test_builder_expand_flash_cmd() -> None:
     b = NuttXBuilder(copy.deepcopy(conf_dir))
     core_cfg = {
@@ -283,7 +312,8 @@ def 
test_builder_regenerates_config_after_kconfig_overrides() -> None:
         "--target",
         "olddefconfig",
     ]
-    assert calls[2] == ["cmake", "--build", "bbb/product-xxx-dummy"]
+    assert calls[0][:2] == calls[2][:2]
+    assert calls[3] == ["cmake", "--build", "bbb/product-xxx-dummy"]
 
 
 def test_builder_run_build_target_passes_env() -> None:
@@ -655,11 +685,14 @@ def test_builder_applies_kv_before_build() -> None:
         def run_command_capture(cmd, env):
             calls.append(cmd)
             if "--build" not in cmd:
+                # cmake generates .config only when it does not exist yet
                 expected_build_path.mkdir(parents=True, exist_ok=True)
-                expected_conf_path.write_text(
-                    "# CONFIG_TEST_BOOL is not set\n" "CONFIG_TEST_STR=old\n",
-                    encoding="utf-8",
-                )
+                if not expected_conf_path.exists():
+                    expected_conf_path.write_text(
+                        "# CONFIG_TEST_BOOL is not set\n"
+                        "CONFIG_TEST_STR=old\n",
+                        encoding="utf-8",
+                    )
             else:
                 cfg_text = expected_conf_path.read_text(encoding="utf-8")
                 assert "CONFIG_TEST_BOOL=y\n" in cfg_text
@@ -679,11 +712,12 @@ def test_builder_applies_kv_before_build() -> None:
         with patch("ntfc.builder.logger.info", side_effect=logs.append):
             b.build_all()
 
-        assert len(calls) == 3
+        # configure, olddefconfig and reconfigure after overrides, build
+        assert len(calls) == 4
         assert calls[0][0] == "cmake"
-        assert calls[1][:2] == ["cmake", "--build"]
-        assert calls[1][-2:] == ["--target", "olddefconfig"]
-        assert calls[2][:2] == ["cmake", "--build"]
+        assert calls[1][-1] == "olddefconfig"
+        assert calls[2][0] == "cmake"
+        assert calls[3][:2] == ["cmake", "--build"]
         assert any(
             "Applying Kconfig overrides before build:" == msg for msg in logs
         )

Reply via email to