[espidf] Use forward slashes in generated component CMakeLists (#17965)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Jonathan Swoboda
2026-07-30 20:34:04 -04:00
committed by GitHub
co-authored by J. Nick Koston
parent 963a3379a6
commit 44f08d15db
2 changed files with 46 additions and 11 deletions
+11 -3
View File
@@ -93,6 +93,14 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
# In CMakeLists.txt, backslashes need to be escaped
return f'"{str(p)}"'.replace("\\", "\\\\")
def escape_path(p: PathType) -> str:
# CMake uses forward slashes for paths on every platform and treats
# backslashes as escape characters. On Windows os.path.relpath yields
# backslash paths, which break CMake's list re-parsing (e.g. "\b" in
# "src\backend" is an invalid character escape). Emit forward slashes,
# which Windows accepts too, so the generated CMakeLists is portable.
return f'"{str(p).replace(os.sep, "/")}"'
# Extract the values
build_src_dir = component.data.get("build", {}).get("srcDir", None)
if not build_src_dir:
@@ -174,10 +182,10 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
# Generate the component
content = "idf_component_register(\n"
if build_src_files:
str_srcs = " ".join([escape_entry(p) for p in sorted(build_src_files)])
str_srcs = " ".join([escape_path(p) for p in sorted(build_src_files)])
content += f" SRCS {str_srcs}\n"
if build_include_dirs:
str_include_dirs = " ".join([escape_entry(p) for p in build_include_dirs])
str_include_dirs = " ".join([escape_path(p) for p in build_include_dirs])
content += f" INCLUDE_DIRS {str_include_dirs}\n"
# Project-managed and built-in component lists are set per-project
# via idf_build_set_property in the top-level CMakeLists; expanded
@@ -212,7 +220,7 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
if link_directories:
content += "target_link_directories(${COMPONENT_LIB} INTERFACE\n"
for link_directory in link_directories:
str_build_flag = escape_entry(link_directory)
str_build_flag = escape_path(link_directory)
content += f" {str_build_flag}\n"
content += ")\n"
+35 -8
View File
@@ -169,30 +169,57 @@ def test_generate_cmakelists_txt_with_flags(tmp_component, tmp_path):
}
content = generate_cmakelists_txt(tmp_component)
sep = "\\\\" if os.name == "nt" else "/"
# Paths are always emitted with forward slashes so the CMakeLists is
# portable; on Windows os.path.relpath would otherwise yield backslashes
# that break CMake's list re-parsing.
assert (
content
== f"""idf_component_register(
SRCS "src{sep}main.c"
== """idf_component_register(
SRCS "src/main.c"
INCLUDE_DIRS "src"
REQUIRES dep ${{ESPHOME_PROJECT_MANAGED_COMPONENTS}} ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}}
REQUIRES dep ${ESPHOME_PROJECT_MANAGED_COMPONENTS} ${ESPHOME_PROJECT_BUILTIN_COMPONENTS}
)
target_compile_options(${{COMPONENT_LIB}} PUBLIC
target_compile_options(${COMPONENT_LIB} PUBLIC
"-DTEST"
)
target_compile_options(${{COMPONENT_LIB}} PRIVATE
target_compile_options(${COMPONENT_LIB} PRIVATE
"-Wall"
)
target_link_directories(${{COMPONENT_LIB}} INTERFACE
target_link_directories(${COMPONENT_LIB} INTERFACE
"lib"
)
target_link_libraries(${{COMPONENT_LIB}} INTERFACE
target_link_libraries(${COMPONENT_LIB} INTERFACE
"mylib"
)
"""
)
def test_generate_cmakelists_txt_uses_forward_slashes_on_windows(
tmp_component, monkeypatch: pytest.MonkeyPatch
) -> None:
# os.path.relpath yields backslash paths on Windows, which CMake rejects
# when it re-parses the SRCS list (e.g. "\b" in "src\backend" is an invalid
# character escape). Simulate that output and confirm the generated
# CMakeLists normalizes the separators to forward slashes.
src_dir = tmp_component.path / "src" / "backend"
src_dir.mkdir(parents=True)
(src_dir / "cipher.c").write_text("int f() {}")
tmp_component.data = {}
monkeypatch.setattr("esphome.espidf.component.os.sep", "\\")
monkeypatch.setattr(
"esphome.espidf.component.os.path.relpath",
lambda *args, **kwargs: "src\\backend\\cipher.c",
)
content = generate_cmakelists_txt(tmp_component)
assert 'SRCS "src/backend/cipher.c"' in content
assert "\\" not in content
def test_generate_cmakelists_txt_multi_token_flag(tmp_component):
# PlatformIO shell-lexes each build.flags entry, so a single entry can
# carry a flag and its argument. The generated CMakeLists must emit them