Revert ninja command quoting (ninja escapes its own path variables); share the lib_ignore helper

This commit is contained in:
J. Nick Koston
2026-08-20 04:31:01 -05:00
parent c579a2792f
commit db010d486e
5 changed files with 47 additions and 30 deletions
+5 -6
View File
@@ -31,6 +31,8 @@ from esphome.platformio.library import (
collect_filtered_files,
convert_libraries,
ensure_list,
is_lib_ignored,
lib_ignore_set,
normalize_dependencies,
parse_library_properties,
)
@@ -134,12 +136,9 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
external: list[Library] = []
# PlatformIO's lib_ignore covers framework-bundled libraries too; the
# shared converter only filters the registry/git ones.
lib_ignore = {
ignore.split("/")[-1].lower()
for ignore in CORE.platformio_options.get("lib_ignore", [])
}
lib_ignore = lib_ignore_set()
for library in CORE.platformio_libraries.values():
if library.name and library.name.split("/")[-1].lower() in lib_ignore:
if is_lib_ignored(library.name, lib_ignore):
continue
# A version pin means a registry package ("pngle@1.1.0"), never a
# framework-bundled library.
@@ -174,7 +173,7 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
or not (framework_path / "libraries" / name).is_dir()
):
continue
if name.lower() in lib_ignore:
if is_lib_ignored(name, lib_ignore):
continue
try:
check_library_data(dep, ESP8266_PLATFORM, "arduino")
+1 -3
View File
@@ -121,9 +121,7 @@ def _pio_system() -> str:
return "linux_x86_64"
def _registry_download(
package: str, version: str
) -> tuple[str, str | None, int | None]:
def _registry_download(package: str, version: str) -> tuple[str, str, int | None]:
"""Resolve a package's download URL, sha256, and size via the PIO registry."""
import requests
+14 -11
View File
@@ -493,10 +493,9 @@ def write_project(paths: dict[str, Path]) -> bool:
# as under PlatformIO (a silently ignored ``build_unflags: -Os`` would
# diverge between the toolchains).
unflags = set(CORE.build_unflags)
if unflags:
cflags = [f for f in cflags if f not in unflags]
cxxflags = [f for f in cxxflags if f not in unflags]
asflags = [f for f in asflags if f not in unflags]
cflags = [f for f in cflags if f not in unflags]
cxxflags = [f for f in cxxflags if f not in unflags]
asflags = [f for f in asflags if f not in unflags]
link_flags = [f for f in _LINKFLAGS if f not in unflags]
if esp8266_data.get(KEY_SCANF_FLOAT):
@@ -522,6 +521,10 @@ def write_project(paths: dict[str, Path]) -> bool:
build_tool = Path(__file__).parent.parent / "arduino8266" / "build_tool.py"
ccache = ccache_path()
# $in/$out stay unquoted in the rule commands: ninja shell-escapes its
# built-in path variables itself when expanding a command (POSIX and
# Windows), so adding quotes would wrap ninja's own quoting and break
# space-containing paths. Only literal paths need _q().
lines = [
"# Auto-generated by ESPHome",
"ninja_required_version = 1.5",
@@ -532,35 +535,35 @@ def write_project(paths: dict[str, Path]) -> bool:
f"ccache = {_q(ccache) if ccache else ''}",
"",
"rule cc",
' command = $ccache $cc -MMD -MF "$out.d" $cflags $flags -c "$in" -o "$out"',
" command = $ccache $cc -MMD -MF $out.d $cflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CC $out",
"rule cxx",
' command = $ccache $cxx -MMD -MF "$out.d" $cxxflags $flags -c "$in" -o "$out"',
" command = $ccache $cxx -MMD -MF $out.d $cxxflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CXX $out",
"rule asm",
' command = $ccache $cc -MMD -MF "$out.d" -x assembler-with-cpp $asflags $flags -c "$in" -o "$out"',
" command = $ccache $cc -MMD -MF $out.d -x assembler-with-cpp $asflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = AS $out",
"rule ar",
f' command = $python $buildtool ar {_q(toolchain_bin / "xtensa-lx106-elf-ar")} "$out" "$out.rsp"',
f" command = $python $buildtool ar {_q(toolchain_bin / 'xtensa-lx106-elf-ar')} $out $out.rsp",
" rspfile = $out.rsp",
" rspfile_content = $in_newline",
" description = AR $out",
"rule link",
' command = $cxx -o "$out" $linkflags @"$out.rsp" $libdirflags -Wl,--start-group $archives $libflags -Wl,--end-group',
" command = $cxx -o $out $linkflags @$out.rsp $libdirflags -Wl,--start-group $archives $libflags -Wl,--end-group",
" rspfile = $out.rsp",
" rspfile_content = $in_newline",
" description = LINK $out",
"rule elf2bin",
f' command = $python {_q(framework / "tools" / "elf2bin.py")} --eboot {_q(framework / "bootloaders" / "eboot" / "eboot.elf")} --app "$in" --flash_mode {esp8266_data[KEY_FLASH_MODE]} --flash_freq 40 --flash_size {_flash_size_str(flash_ld_name)} --path {_q(toolchain_bin)} --out "$out"',
f" command = $python {_q(framework / 'tools' / 'elf2bin.py')} --eboot {_q(framework / 'bootloaders' / 'eboot' / 'eboot.elf')} --app $in --flash_mode {esp8266_data[KEY_FLASH_MODE]} --flash_freq 40 --flash_size {_flash_size_str(flash_ld_name)} --path {_q(toolchain_bin)} --out $out",
" description = BIN $out",
"rule copy",
' command = $python $buildtool copy "$in" "$out"',
" command = $python $buildtool copy $in $out",
" description = COPY $out",
"",
f"cflags = {' '.join(cflags)}",
+20 -7
View File
@@ -688,6 +688,24 @@ def _node_key(
return name, "registry", (owner, pkgname)
def lib_ignore_set() -> set[str]:
"""The ``lib_ignore`` names from ``esphome->platformio_options``,
normalized to lowercase short names (the part after the ``/``)."""
return {
name.split("/")[-1].lower()
for name in CORE.platformio_options.get("lib_ignore", [])
}
def is_lib_ignored(name: str | None, lib_ignore: set[str]) -> bool:
"""Whether ``name`` matches the normalized ``lib_ignore`` set."""
return (
bool(lib_ignore)
and name is not None
and (name.split("/")[-1].lower() in lib_ignore)
)
def convert_libraries(
libraries: list[Library], backend: LibraryBackend
) -> list[ConvertedLibrary]:
@@ -713,10 +731,7 @@ def convert_libraries(
"""
nodes: dict[str, _LibNode] = {}
lib_ignore = {
name.split("/")[-1].lower()
for name in CORE.platformio_options.get("lib_ignore", [])
}
lib_ignore = lib_ignore_set()
# The generated build files inside the shared cache bake in the dependency
# wiring, which lib_ignore changes; salt the cache path so configs with
@@ -729,9 +744,7 @@ def convert_libraries(
)
def is_ignored(name: str | None) -> bool:
if not lib_ignore or name is None:
return False
return name.split("/")[-1].lower() in lib_ignore
return is_lib_ignored(name, lib_ignore)
def add_spec(name: str | None, version: str | None, repository: str | None) -> str:
key, kind, locator = _node_key(name, version, repository)
@@ -249,9 +249,13 @@ def test_write_project_link_line_and_exclusions(tmp_path: Path) -> None:
assert "-T eagle.flash.4m.ld" in content
# scanf float disabled: the forced-link flag must not appear
assert "_scanf_float" not in content
# Compile inputs and outputs are quoted (space-containing cache paths)
assert '-c "$in" -o "$out"' in content
assert '--app "$in"' in content
# $in/$out must stay UNQUOTED: ninja shell-escapes its built-in path
# variables itself, so added quotes would wrap ninja's quoting and break
# space-containing paths.
assert "-c $in -o $out" in content
assert "--app $in --flash_mode" in content
assert '"$in"' not in content
assert '"$out"' not in content
# -L/-l from esphome build_flags reach the link line, not the compiles
assert '-L"/opt/blobs"' in content
assert "-luser_blob" in content