mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
[core] Add add_cmake_arg (#18498)
This commit is contained in:
@@ -16,6 +16,7 @@ from esphome.components.esp32 import (
|
||||
KEY_PATH,
|
||||
KEY_REF,
|
||||
KEY_REPO,
|
||||
register_exclude_components_cmake_arg,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import KEY_CORE
|
||||
@@ -137,6 +138,27 @@ def test_get_project_cmakelists_full_emits_builtin_components_property(
|
||||
assert "JPEGDEC APPEND" not in content
|
||||
|
||||
|
||||
def test_get_project_cmakelists_emits_cmake_args() -> None:
|
||||
"""Args registered via CORE.add_cmake_arg() are emitted as set() lines,
|
||||
on minimal writes too."""
|
||||
CORE.add_cmake_arg("EXECUTABLE_COMPONENT_NAME", "src")
|
||||
|
||||
content = _render(minimal=True)
|
||||
|
||||
assert 'set(EXECUTABLE_COMPONENT_NAME "src")' in content
|
||||
|
||||
|
||||
def test_get_project_cmakelists_escapes_backslashes_in_cmake_args() -> None:
|
||||
"""Backslashes (the only character escaping applies to; the rest are
|
||||
rejected at registration) are doubled so CMake reads the value back
|
||||
verbatim."""
|
||||
CORE.add_cmake_arg("MY_PATH", r"C:\esp\idf")
|
||||
|
||||
content = _render(minimal=True)
|
||||
|
||||
assert r'set(MY_PATH "C:\\esp\\idf")' in content
|
||||
|
||||
|
||||
def test_get_project_cmakelists_emits_exclude_components(tmp_path: Path) -> None:
|
||||
"""Excluded components are passed to IDF via EXCLUDE_COMPONENTS and are
|
||||
dropped from ESPHOME_PROJECT_BUILTIN_COMPONENTS even when a stale
|
||||
@@ -151,6 +173,7 @@ def test_get_project_cmakelists_emits_exclude_components(tmp_path: Path) -> None
|
||||
},
|
||||
)
|
||||
CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] = {"unity", "esp_lcd"}
|
||||
register_exclude_components_cmake_arg()
|
||||
|
||||
content = _render()
|
||||
|
||||
@@ -169,6 +192,7 @@ def test_get_project_cmakelists_minimal_emits_exclude_components() -> None:
|
||||
"""The discovery (minimal) write also excludes components so they never
|
||||
register in project_description.json."""
|
||||
CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] = {"unity"}
|
||||
register_exclude_components_cmake_arg()
|
||||
|
||||
content = _render(minimal=True)
|
||||
|
||||
@@ -177,6 +201,8 @@ def test_get_project_cmakelists_minimal_emits_exclude_components() -> None:
|
||||
|
||||
def test_get_project_cmakelists_no_exclude_components_line_when_empty() -> None:
|
||||
"""No EXCLUDE_COMPONENTS line at all when nothing is excluded."""
|
||||
register_exclude_components_cmake_arg()
|
||||
|
||||
content = _render()
|
||||
|
||||
assert "EXCLUDE_COMPONENTS" not in content
|
||||
@@ -197,6 +223,7 @@ def test_include_builtin_idf_component_removes_exclusion() -> None:
|
||||
|
||||
assert get_excluded_builtin_components() == ["unity"]
|
||||
|
||||
register_exclude_components_cmake_arg()
|
||||
content = _render()
|
||||
|
||||
assert 'set(EXCLUDE_COMPONENTS "unity")' in content
|
||||
|
||||
@@ -169,6 +169,7 @@ def clean_core(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(CORE, "platformio_libraries", {})
|
||||
monkeypatch.setattr(CORE, "build_flags", set())
|
||||
monkeypatch.setattr(CORE, "build_unflags", set())
|
||||
monkeypatch.setattr(CORE, "cmake_args", {})
|
||||
|
||||
|
||||
def test_get_ini_content_pins_cpp_standard(
|
||||
@@ -202,6 +203,49 @@ def test_get_ini_content_no_cpp_standard(
|
||||
assert "-std=" not in content
|
||||
|
||||
|
||||
def test_get_ini_content_emits_cmake_args(
|
||||
clean_core: None, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Registered args are space-joined into one option, sorted by name."""
|
||||
monkeypatch.setattr(
|
||||
CORE,
|
||||
"cmake_args",
|
||||
{"EXECUTABLE_COMPONENT_NAME": "src", "EXCLUDE_COMPONENTS": "unity"},
|
||||
)
|
||||
|
||||
content = platformio.get_ini_content()
|
||||
|
||||
assert (
|
||||
"board_build.cmake_extra_args = "
|
||||
"-DEXCLUDE_COMPONENTS=unity -DEXECUTABLE_COMPONENT_NAME=src" in content
|
||||
)
|
||||
|
||||
|
||||
def test_get_ini_content_no_cmake_option_when_no_args(clean_core: None) -> None:
|
||||
"""No board_build.cmake_extra_args line at all when nothing registered
|
||||
(ESP8266/RP2040/LibreTiny builds must not get a blank option)."""
|
||||
content = platformio.get_ini_content()
|
||||
|
||||
assert "board_build.cmake_extra_args" not in content
|
||||
|
||||
|
||||
def test_get_ini_content_overwrites_list_valued_user_cmake_option(
|
||||
clean_core: None, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""A user-supplied board_build.cmake_extra_args may be a list; the
|
||||
registered args must replace it without tripping add_platformio_option's
|
||||
list-append assert."""
|
||||
monkeypatch.setattr(
|
||||
CORE, "platformio_options", {"board_build.cmake_extra_args": ["-DFOO=1"]}
|
||||
)
|
||||
monkeypatch.setattr(CORE, "cmake_args", {"EXECUTABLE_COMPONENT_NAME": "src"})
|
||||
|
||||
content = platformio.get_ini_content()
|
||||
|
||||
assert "board_build.cmake_extra_args = -DEXECUTABLE_COMPONENT_NAME=src" in content
|
||||
assert "-DFOO=1" not in content
|
||||
|
||||
|
||||
def test_write_cxx_flags_script_emits_registered_flags(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
|
||||
@@ -990,3 +990,35 @@ class TestEsphomeCore:
|
||||
)
|
||||
# The unflag is still recorded either way.
|
||||
assert target.build_unflags == {"-fno-rtti", "-fno-exceptions"}
|
||||
|
||||
def test_add_cmake_arg(self, target) -> None:
|
||||
target.add_cmake_arg("EXCLUDE_COMPONENTS", "unity;esp_lcd")
|
||||
assert target.cmake_args == {"EXCLUDE_COMPONENTS": "unity;esp_lcd"}
|
||||
|
||||
@pytest.mark.parametrize("name", ["", "BAD NAME", 'A"B', "A(B)", "1ABC"])
|
||||
def test_add_cmake_arg__rejects_invalid_name(self, target, name: str) -> None:
|
||||
with pytest.raises(ValueError, match="Invalid CMake arg name"):
|
||||
target.add_cmake_arg(name, "value")
|
||||
|
||||
@pytest.mark.parametrize("value", ["a b", "a\tb", 'a"b', "a'b", "a${FOO}b"])
|
||||
def test_add_cmake_arg__rejects_invalid_value(self, target, value: str) -> None:
|
||||
"""Whitespace and quotes are rejected (the PlatformIO backend passes
|
||||
args as one space-joined string, which would split such a value), and
|
||||
so is '$' (expanded differently by CMake and PlatformIO)."""
|
||||
with pytest.raises(ValueError, match="must not contain"):
|
||||
target.add_cmake_arg("MY_ARG", value)
|
||||
|
||||
def test_add_cmake_arg__warns_on_overwrite(
|
||||
self, target, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Re-registering with a different value is last-writer-wins; warn so
|
||||
the silently dropped value is diagnosable."""
|
||||
target.add_cmake_arg("MY_ARG", "one")
|
||||
target.add_cmake_arg("MY_ARG", "one")
|
||||
assert "overwriting" not in caplog.text
|
||||
|
||||
target.add_cmake_arg("MY_ARG", "two")
|
||||
assert (
|
||||
"CMake arg MY_ARG already set to one; overwriting with two" in caplog.text
|
||||
)
|
||||
assert target.cmake_args == {"MY_ARG": "two"}
|
||||
|
||||
Reference in New Issue
Block a user