mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[core] Add add_cmake_arg (#18498)
This commit is contained in:
+23
-15
@@ -72,6 +72,13 @@ def has_discovered_components() -> bool:
|
|||||||
return get_available_components() is not None
|
return get_available_components() is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _cmake_quote(value: str) -> str:
|
||||||
|
"""Quote a cmake arg value for a set() line. add_cmake_arg rejects
|
||||||
|
whitespace, quotes, and '$', so only backslashes need escaping."""
|
||||||
|
escaped = value.replace("\\", "\\\\")
|
||||||
|
return f'"{escaped}"'
|
||||||
|
|
||||||
|
|
||||||
def get_project_cmakelists(minimal: bool = False) -> str:
|
def get_project_cmakelists(minimal: bool = False) -> str:
|
||||||
"""Generate the top-level CMakeLists.txt for ESP-IDF project.
|
"""Generate the top-level CMakeLists.txt for ESP-IDF project.
|
||||||
|
|
||||||
@@ -114,6 +121,15 @@ def get_project_cmakelists(minimal: bool = False) -> str:
|
|||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# CMake variables registered via cg.add_cmake_arg(). Emitted before
|
||||||
|
# include(project.cmake) so values like EXCLUDE_COMPONENTS are already
|
||||||
|
# set when project.cmake seeds the component list, and on minimal
|
||||||
|
# (discovery) writes too so excluded components never register.
|
||||||
|
cmake_args = "\n".join(
|
||||||
|
f"set({name} {_cmake_quote(value)})"
|
||||||
|
for name, value in sorted(CORE.cmake_args.items())
|
||||||
|
)
|
||||||
|
|
||||||
# Per-project list exposed as a CMake variable so converted PIO libs
|
# Per-project list exposed as a CMake variable so converted PIO libs
|
||||||
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
|
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
|
||||||
# project-specific names into their cached CMakeLists.
|
# project-specific names into their cached CMakeLists.
|
||||||
@@ -129,18 +145,6 @@ def get_project_cmakelists(minimal: bool = False) -> str:
|
|||||||
for name in get_managed_component_require_names()
|
for name in get_managed_component_require_names()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Components excluded from the build (DEFAULT_EXCLUDED_IDF_COMPONENTS
|
|
||||||
# minus per-component re-includes). project.cmake reads the plain
|
|
||||||
# EXCLUDE_COMPONENTS variable when seeding the component list, so this
|
|
||||||
# must be set before project(). Emitted on minimal writes too so the
|
|
||||||
# discovery reconfigure never registers the excluded components.
|
|
||||||
excluded_components = get_excluded_builtin_components()
|
|
||||||
exclude_components_var = (
|
|
||||||
f'set(EXCLUDE_COMPONENTS "{";".join(excluded_components)}")'
|
|
||||||
if excluded_components
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Built-in IDF components exposed via our own property (not IDF's
|
# Built-in IDF components exposed via our own property (not IDF's
|
||||||
# __COMPONENT_REQUIRES_COMMON, which would append them to every
|
# __COMPONENT_REQUIRES_COMMON, which would append them to every
|
||||||
# component's REQUIRES including real IDF components). Referenced by
|
# component's REQUIRES including real IDF components). Referenced by
|
||||||
@@ -150,13 +154,17 @@ def get_project_cmakelists(minimal: bool = False) -> str:
|
|||||||
# project_description.json from a build without exclusions may still
|
# project_description.json from a build without exclusions may still
|
||||||
# list them, and requiring an excluded component pulls it back into
|
# list them, and requiring an excluded component pulls it back into
|
||||||
# the build (IDF requirement expansion overrides EXCLUDE_COMPONENTS).
|
# the build (IDF requirement expansion overrides EXCLUDE_COMPONENTS).
|
||||||
|
# Derived from the EXCLUDE_COMPONENTS cmake arg emitted above so the
|
||||||
|
# two can never disagree within one generated file.
|
||||||
builtin_components_property = (
|
builtin_components_property = (
|
||||||
""
|
""
|
||||||
if minimal
|
if minimal
|
||||||
else "\n".join(
|
else "\n".join(
|
||||||
f"idf_build_set_property(ESPHOME_PROJECT_BUILTIN_COMPONENTS {name} APPEND)"
|
f"idf_build_set_property(ESPHOME_PROJECT_BUILTIN_COMPONENTS {name} APPEND)"
|
||||||
for name in sorted(
|
for name in sorted(
|
||||||
set(get_available_components() or []).difference(excluded_components)
|
set(get_available_components() or []).difference(
|
||||||
|
CORE.cmake_args.get("EXCLUDE_COMPONENTS", "").split(";")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -184,9 +192,9 @@ set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
|
|||||||
set(IDF_TARGET {idf_target})
|
set(IDF_TARGET {idf_target})
|
||||||
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
|
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
|
||||||
|
|
||||||
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
|
{cmake_args}
|
||||||
|
|
||||||
{exclude_components_var}
|
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
{cpp_standard_options}
|
{cpp_standard_options}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,17 @@ def get_ini_content():
|
|||||||
# Add extra script for C++ flags
|
# Add extra script for C++ flags
|
||||||
CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
|
CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
|
||||||
|
|
||||||
|
# Add CMake args. A user-supplied value (str or list) is deliberately
|
||||||
|
# replaced; this option was always overwritten at FINAL priority.
|
||||||
|
if CORE.cmake_args:
|
||||||
|
CORE.add_platformio_option(
|
||||||
|
"board_build.cmake_extra_args",
|
||||||
|
" ".join(
|
||||||
|
f"-D{name}={value}" for name, value in sorted(CORE.cmake_args.items())
|
||||||
|
),
|
||||||
|
replace=True,
|
||||||
|
)
|
||||||
|
|
||||||
content = "[platformio]\n"
|
content = "[platformio]\n"
|
||||||
content += f"description = ESPHome {__version__}\n"
|
content += f"description = ESPHome {__version__}\n"
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
|||||||
add,
|
add,
|
||||||
add_build_flag,
|
add_build_flag,
|
||||||
add_build_unflag,
|
add_build_unflag,
|
||||||
|
add_cmake_arg,
|
||||||
add_cxx_build_flag,
|
add_cxx_build_flag,
|
||||||
add_define,
|
add_define,
|
||||||
add_global,
|
add_global,
|
||||||
|
|||||||
@@ -760,9 +760,10 @@ def include_builtin_idf_component(name: str) -> None:
|
|||||||
def get_excluded_builtin_components() -> list[str]:
|
def get_excluded_builtin_components() -> list[str]:
|
||||||
"""Return the sorted built-in IDF components excluded from the build.
|
"""Return the sorted built-in IDF components excluded from the build.
|
||||||
|
|
||||||
Single accessor for both build writers: the PlatformIO path passes it as
|
The set reaches both build writers as the ``EXCLUDE_COMPONENTS`` CMake
|
||||||
``-DEXCLUDE_COMPONENTS`` and the native ESP-IDF path emits it into the
|
arg (registered via ``cg.add_cmake_arg`` at FINAL priority); the native
|
||||||
generated CMakeLists.
|
ESP-IDF writer also reads it directly to filter the built-in component
|
||||||
|
list.
|
||||||
"""
|
"""
|
||||||
return sorted(CORE.data.get(KEY_ESP32, {}).get(KEY_EXCLUDE_COMPONENTS, ()))
|
return sorted(CORE.data.get(KEY_ESP32, {}).get(KEY_EXCLUDE_COMPONENTS, ()))
|
||||||
|
|
||||||
@@ -2148,14 +2149,16 @@ def _configure_lwip_max_sockets(conf: dict) -> None:
|
|||||||
add_idf_sdkconfig_option("CONFIG_LWIP_MAX_SOCKETS", max_sockets)
|
add_idf_sdkconfig_option("CONFIG_LWIP_MAX_SOCKETS", max_sockets)
|
||||||
|
|
||||||
|
|
||||||
|
def register_exclude_components_cmake_arg() -> None:
|
||||||
|
"""Register the current exclusion set as the EXCLUDE_COMPONENTS cmake arg."""
|
||||||
|
if excluded := get_excluded_builtin_components():
|
||||||
|
cg.add_cmake_arg("EXCLUDE_COMPONENTS", ";".join(excluded))
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(CoroPriority.FINAL)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
async def _write_exclude_components() -> None:
|
async def _write_exclude_components() -> None:
|
||||||
"""Write EXCLUDE_COMPONENTS cmake arg after all components have registered exclusions."""
|
"""Write EXCLUDE_COMPONENTS cmake arg after all components have registered exclusions."""
|
||||||
if excluded := get_excluded_builtin_components():
|
register_exclude_components_cmake_arg()
|
||||||
cg.add_platformio_option(
|
|
||||||
"board_build.cmake_extra_args",
|
|
||||||
f"-DEXCLUDE_COMPONENTS={';'.join(excluded)}",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(CoroPriority.FINAL)
|
@coroutine_with_priority(CoroPriority.FINAL)
|
||||||
|
|||||||
@@ -641,6 +641,8 @@ class EsphomeCore:
|
|||||||
self.platformio_libraries: dict[str, Library] = {}
|
self.platformio_libraries: dict[str, Library] = {}
|
||||||
# A set of build flags to set in the platformio project
|
# A set of build flags to set in the platformio project
|
||||||
self.build_flags: set[str] = set()
|
self.build_flags: set[str] = set()
|
||||||
|
# A map of CMake args to apply to build systems that use CMake.
|
||||||
|
self.cmake_args: dict[str, str] = {}
|
||||||
# A set of build flags that apply to C++ compiles only (CXXFLAGS /
|
# A set of build flags that apply to C++ compiles only (CXXFLAGS /
|
||||||
# CXX_COMPILE_OPTIONS), for flags GCC rejects or warns about on C
|
# CXX_COMPILE_OPTIONS), for flags GCC rejects or warns about on C
|
||||||
self.cxx_build_flags: set[str] = set()
|
self.cxx_build_flags: set[str] = set()
|
||||||
@@ -704,6 +706,7 @@ class EsphomeCore:
|
|||||||
self.global_statements = []
|
self.global_statements = []
|
||||||
self.platformio_libraries = {}
|
self.platformio_libraries = {}
|
||||||
self.build_flags = set()
|
self.build_flags = set()
|
||||||
|
self.cmake_args = {}
|
||||||
self.cxx_build_flags = set()
|
self.cxx_build_flags = set()
|
||||||
self.build_unflags = set()
|
self.build_unflags = set()
|
||||||
self.cpp_standard = None
|
self.cpp_standard = None
|
||||||
@@ -1062,6 +1065,30 @@ class EsphomeCore:
|
|||||||
_LOGGER.debug("Adding build flag: %s", build_flag)
|
_LOGGER.debug("Adding build flag: %s", build_flag)
|
||||||
return build_flag
|
return build_flag
|
||||||
|
|
||||||
|
def add_cmake_arg(self, name: str, value: str) -> None:
|
||||||
|
"""Register a CMake variable for CMake-based toolchains.
|
||||||
|
|
||||||
|
The value must not contain whitespace or quotes (the PlatformIO
|
||||||
|
backend passes all args to CMake as a single space-joined string
|
||||||
|
of ``-DNAME=VALUE`` pairs) or ``$`` (expanded by CMake on the
|
||||||
|
ESP-IDF path but interpolated differently or passed through by
|
||||||
|
PlatformIO).
|
||||||
|
"""
|
||||||
|
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name):
|
||||||
|
raise ValueError(f"Invalid CMake arg name: {name!r}")
|
||||||
|
if re.search(r"[\s\"'$]", value):
|
||||||
|
raise ValueError(
|
||||||
|
f"CMake arg {name} value {value!r} must not contain "
|
||||||
|
"whitespace, quotes, or '$'"
|
||||||
|
)
|
||||||
|
old = self.cmake_args.get(name)
|
||||||
|
if old is not None and old != value:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"CMake arg %s already set to %s; overwriting with %s", name, old, value
|
||||||
|
)
|
||||||
|
self.cmake_args[name] = value
|
||||||
|
_LOGGER.debug("Adding CMake arg: %s=%s", name, value)
|
||||||
|
|
||||||
def add_cxx_build_flag(self, build_flag: str) -> str:
|
def add_cxx_build_flag(self, build_flag: str) -> str:
|
||||||
self.cxx_build_flags.add(build_flag)
|
self.cxx_build_flags.add(build_flag)
|
||||||
_LOGGER.debug("Adding C++ build flag: %s", build_flag)
|
_LOGGER.debug("Adding C++ build flag: %s", build_flag)
|
||||||
@@ -1091,10 +1118,14 @@ class EsphomeCore:
|
|||||||
_LOGGER.debug("Adding define: %s", define)
|
_LOGGER.debug("Adding define: %s", define)
|
||||||
return define
|
return define
|
||||||
|
|
||||||
def add_platformio_option(self, key: str, value: str | list[str]) -> None:
|
def add_platformio_option(
|
||||||
|
self, key: str, value: str | list[str], *, replace: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""Set a platformio.ini option; list values append to an existing list
|
||||||
|
unless ``replace`` is True, which overwrites any existing value."""
|
||||||
new_val = value
|
new_val = value
|
||||||
old_val = self.platformio_options.get(key)
|
old_val = self.platformio_options.get(key)
|
||||||
if isinstance(old_val, list):
|
if not replace and isinstance(old_val, list):
|
||||||
assert isinstance(value, list)
|
assert isinstance(value, list)
|
||||||
new_val = old_val + value
|
new_val = old_val + value
|
||||||
self.platformio_options[key] = new_val
|
self.platformio_options[key] = new_val
|
||||||
|
|||||||
@@ -699,6 +699,11 @@ def add_build_flag(build_flag: str):
|
|||||||
CORE.add_build_flag(build_flag)
|
CORE.add_build_flag(build_flag)
|
||||||
|
|
||||||
|
|
||||||
|
def add_cmake_arg(name: str, value: str) -> None:
|
||||||
|
"""Add a CMake arg for CMake-based toolchains; see ``EsphomeCore.add_cmake_arg``."""
|
||||||
|
CORE.add_cmake_arg(name, value)
|
||||||
|
|
||||||
|
|
||||||
def add_cxx_build_flag(build_flag: str) -> None:
|
def add_cxx_build_flag(build_flag: str) -> None:
|
||||||
"""Add a global build flag that applies to C++ compiles only.
|
"""Add a global build flag that applies to C++ compiles only.
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from esphome.components.esp32 import (
|
|||||||
KEY_PATH,
|
KEY_PATH,
|
||||||
KEY_REF,
|
KEY_REF,
|
||||||
KEY_REPO,
|
KEY_REPO,
|
||||||
|
register_exclude_components_cmake_arg,
|
||||||
)
|
)
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import KEY_CORE
|
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
|
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:
|
def test_get_project_cmakelists_emits_exclude_components(tmp_path: Path) -> None:
|
||||||
"""Excluded components are passed to IDF via EXCLUDE_COMPONENTS and are
|
"""Excluded components are passed to IDF via EXCLUDE_COMPONENTS and are
|
||||||
dropped from ESPHOME_PROJECT_BUILTIN_COMPONENTS even when a stale
|
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"}
|
CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] = {"unity", "esp_lcd"}
|
||||||
|
register_exclude_components_cmake_arg()
|
||||||
|
|
||||||
content = _render()
|
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
|
"""The discovery (minimal) write also excludes components so they never
|
||||||
register in project_description.json."""
|
register in project_description.json."""
|
||||||
CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] = {"unity"}
|
CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS] = {"unity"}
|
||||||
|
register_exclude_components_cmake_arg()
|
||||||
|
|
||||||
content = _render(minimal=True)
|
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:
|
def test_get_project_cmakelists_no_exclude_components_line_when_empty() -> None:
|
||||||
"""No EXCLUDE_COMPONENTS line at all when nothing is excluded."""
|
"""No EXCLUDE_COMPONENTS line at all when nothing is excluded."""
|
||||||
|
register_exclude_components_cmake_arg()
|
||||||
|
|
||||||
content = _render()
|
content = _render()
|
||||||
|
|
||||||
assert "EXCLUDE_COMPONENTS" not in content
|
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"]
|
assert get_excluded_builtin_components() == ["unity"]
|
||||||
|
|
||||||
|
register_exclude_components_cmake_arg()
|
||||||
content = _render()
|
content = _render()
|
||||||
|
|
||||||
assert 'set(EXCLUDE_COMPONENTS "unity")' in content
|
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, "platformio_libraries", {})
|
||||||
monkeypatch.setattr(CORE, "build_flags", set())
|
monkeypatch.setattr(CORE, "build_flags", set())
|
||||||
monkeypatch.setattr(CORE, "build_unflags", set())
|
monkeypatch.setattr(CORE, "build_unflags", set())
|
||||||
|
monkeypatch.setattr(CORE, "cmake_args", {})
|
||||||
|
|
||||||
|
|
||||||
def test_get_ini_content_pins_cpp_standard(
|
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
|
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(
|
def test_write_cxx_flags_script_emits_registered_flags(
|
||||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -990,3 +990,35 @@ class TestEsphomeCore:
|
|||||||
)
|
)
|
||||||
# The unflag is still recorded either way.
|
# The unflag is still recorded either way.
|
||||||
assert target.build_unflags == {"-fno-rtti", "-fno-exceptions"}
|
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