From 7a182ccddbe6184080d03df63f6b9ef94fe52c38 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 29 Sep 2026 21:06:21 +0200 Subject: [PATCH] [espidf] Share one text sdkconfig parser --- esphome/__main__.py | 9 ++++----- esphome/espidf/bootloader.py | 17 +++++++---------- esphome/espidf/toolchain.py | 18 ++++++++++++++++++ tests/unit_tests/test_espidf_toolchain.py | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 01ca8ca5d96..105a9496ba3 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -924,14 +924,13 @@ def _check_and_emit_build_info() -> None: def _get_configured_xtal_freq() -> int | None: """Read the configured crystal frequency from the sdkconfig file.""" + from esphome.espidf.toolchain import parse_sdkconfig + sdkconfig_path = CORE.relative_build_path(f"sdkconfig.{CORE.name}") if not sdkconfig_path.is_file(): return None - with suppress(OSError, ValueError): - content = sdkconfig_path.read_text() - for line in content.splitlines(): - if line.startswith("CONFIG_XTAL_FREQ="): - return int(line.split("=", 1)[1]) + with suppress(OSError, ValueError, KeyError): + return int(parse_sdkconfig(sdkconfig_path)["CONFIG_XTAL_FREQ"]) return None diff --git a/esphome/espidf/bootloader.py b/esphome/espidf/bootloader.py index efc32635162..ae947ee7ca8 100644 --- a/esphome/espidf/bootloader.py +++ b/esphome/espidf/bootloader.py @@ -90,16 +90,13 @@ def _snapshot_blocks_cache(sdkconfig_path: Path) -> bool: build off blocks so those users skip the doomed probe on every compile; the probe still guards against unknown nondeterminism. """ - reproducible = False - for line in sdkconfig_path.read_text(encoding="utf-8").splitlines(): - name, _, value = line.partition("=") - name = name.strip() - value = value.strip().strip('"') - if _SECURE_OPTION.match(name) and value not in _DISABLED_VALUES: - return True - if name == "CONFIG_APP_REPRODUCIBLE_BUILD": - reproducible = value not in _DISABLED_VALUES - return not reproducible + config = toolchain.parse_sdkconfig(sdkconfig_path) + if any( + _SECURE_OPTION.match(name) and value not in _DISABLED_VALUES + for name, value in config.items() + ): + return True + return config.get("CONFIG_APP_REPRODUCIBLE_BUILD", "") in _DISABLED_VALUES def tree_uses_cached_bootloader(build_dir: Path) -> bool: diff --git a/esphome/espidf/toolchain.py b/esphome/espidf/toolchain.py index fd84791e1d7..d3e25c1835f 100644 --- a/esphome/espidf/toolchain.py +++ b/esphome/espidf/toolchain.py @@ -261,6 +261,24 @@ def _load_sdkconfig_json(build_dir: Path) -> dict | None: return config if isinstance(config, dict) else None +def parse_sdkconfig(path: Path) -> dict[str, str]: + """Map each CONFIG_X=value line of a text sdkconfig to its raw value. + + Comment lines ("# CONFIG_X is not set") are skipped and quoted values + are unquoted. + """ + result = {} + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.startswith("CONFIG_"): + continue + name, _, value = line.partition("=") + value = value.strip() + if len(value) >= 2 and value[0] == value[-1] == '"': + value = value[1:-1] + result[name] = value + return result + + def _resolved_c_compiler() -> str | None: """The configured build's resolved C compiler, or None.""" description = read_json_file(_build_dir() / "project_description.json") diff --git a/tests/unit_tests/test_espidf_toolchain.py b/tests/unit_tests/test_espidf_toolchain.py index 878c93a00de..029c266f1a1 100644 --- a/tests/unit_tests/test_espidf_toolchain.py +++ b/tests/unit_tests/test_espidf_toolchain.py @@ -1277,3 +1277,22 @@ def test_resolved_c_compiler_none_without_a_usable_description( build.mkdir(parents=True) (build / "project_description.json").write_text(description) assert toolchain._resolved_c_compiler() is None + + +def test_parse_sdkconfig(tmp_path: Path) -> None: + path = tmp_path / "sdkconfig" + path.write_text( + "# comment\n" + "# CONFIG_DISABLED is not set\n" + "CONFIG_INT=240\n" + 'CONFIG_STR="key.pem"\n' + 'CONFIG_EMPTY=""\n' + "CONFIG_BOOL=y\n" + "not_config=1\n" + ) + assert toolchain.parse_sdkconfig(path) == { + "CONFIG_INT": "240", + "CONFIG_STR": "key.pem", + "CONFIG_EMPTY": "", + "CONFIG_BOOL": "y", + }