Split multi-token build.flags entries when generating ESP-IDF component CMakeLists (#17649)

This commit is contained in:
Brandon Harvey
2026-07-19 12:20:43 -10:00
committed by GitHub
parent 1f93345af8
commit 977376d55c
2 changed files with 62 additions and 0 deletions
+21
View File
@@ -83,6 +83,10 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
Returns: Returns:
str: The complete CMakeLists.txt content as a string str: The complete CMakeLists.txt content as a string
""" """
# Late import: this module loads with the esp32 platform on every
# validate/compile, but shlex is only needed when generating component
# CMakeLists.
import shlex
def escape_entry(p: PathType) -> str: def escape_entry(p: PathType) -> str:
# In CMakeLists.txt, backslashes need to be escaped # In CMakeLists.txt, backslashes need to be escaped
@@ -105,6 +109,23 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
build_flags = ensure_list( build_flags = ensure_list(
component.data.get("build", {}).get("flags", DEFAULT_BUILD_FLAGS) component.data.get("build", {}).get("flags", DEFAULT_BUILD_FLAGS)
) )
# PlatformIO shell-lexes each build.flags entry, so one entry can carry a
# flag and its argument (e.g. "-include cp_custom_alloc.h"). Split the
# same way; emitting such an entry as a single quoted compile option
# hands the compiler one argv with an embedded space.
build_flags = [token for entry in build_flags for token in shlex.split(entry)]
# Re-glue bare -I/-L/-l tokens to their argument ("-I foo" -> "-Ifoo") so
# the prefix classifiers below still route them to INCLUDE_DIRS and the
# link handling.
tokens, build_flags = build_flags, []
i = 0
while i < len(tokens):
if tokens[i] in ("-I", "-L", "-l") and i + 1 < len(tokens):
build_flags.append(tokens[i] + tokens[i + 1])
i += 2
else:
build_flags.append(tokens[i])
i += 1
# List all sources files # List all sources files
build_src_files = collect_filtered_files( build_src_files = collect_filtered_files(
+41
View File
@@ -193,6 +193,47 @@ target_link_libraries(${{COMPONENT_LIB}} INTERFACE
) )
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
# as separate compile options, not one argument with an embedded space.
src_dir = tmp_component.path / "src"
src_dir.mkdir()
(src_dir / "main.c").write_text("int main() {}")
tmp_component.data = {"build": {"flags": ["-include cp_custom_alloc.h", "-DTEST"]}}
content = generate_cmakelists_txt(tmp_component)
assert '"-include cp_custom_alloc.h"' not in content
assert ' "-include"\n "cp_custom_alloc.h"\n' in content
def test_generate_cmakelists_txt_space_separated_classified_flags(tmp_component):
# Space-separated -I/-L/-l entries routed to INCLUDE_DIRS and the link
# handling before the shlex split was added; splitting must not leak
# them into raw compile options.
src_dir = tmp_component.path / "src"
src_dir.mkdir()
(src_dir / "main.c").write_text("int main() {}")
(tmp_component.path / "extra_inc").mkdir()
tmp_component.data = {
"build": {"flags": ["-I extra_inc", "-L extra_lib", "-l extralib", "-DTEST"]}
}
content = generate_cmakelists_txt(tmp_component)
assert 'INCLUDE_DIRS "src" "extra_inc"' in content
assert 'target_link_directories(${COMPONENT_LIB} INTERFACE\n "extra_lib"\n)' in (
content
)
assert 'target_link_libraries(${COMPONENT_LIB} INTERFACE\n "extralib"\n)' in (
content
)
assert '"-I"' not in content
assert '"-L"' not in content
assert '"-l"' not in content
def test_generate_cmakelists_txt_references_project_managed_components_variable( def test_generate_cmakelists_txt_references_project_managed_components_variable(
tmp_component: IDFComponent, tmp_component: IDFComponent,
) -> None: ) -> None: