Share the PlatformIO flag-lexing helper and flatten the build_flags classifier

This commit is contained in:
J. Nick Koston
2026-08-20 05:03:49 -05:00
parent eb3a64829f
commit 7f2515edf1
4 changed files with 45 additions and 53 deletions
+3 -18
View File
@@ -13,7 +13,6 @@ include path.
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass, field
import logging
from pathlib import Path
@@ -33,6 +32,7 @@ from esphome.platformio.library import (
convert_libraries,
ensure_list,
is_lib_ignored,
join_flag_args,
lib_ignore_set,
normalize_dependencies,
parse_library_properties,
@@ -59,21 +59,6 @@ class ArduinoLibrary:
link_flags: list[str] = field(default_factory=list)
def join_flag_args(tokens: Iterable[str], owner: str) -> list[str]:
"""Join a bare ``-I``/``-L``/``-l`` with its following token (PIO lexing)."""
out: list[str] = []
it = iter(tokens)
for tok in it:
if tok in ("-I", "-L", "-l"):
arg = next(it, None)
if arg is None:
_LOGGER.warning("Ignoring trailing '%s' in %s build flags", tok, owner)
break
tok += arg
out.append(tok)
return out
def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
"""Resolve one library's sources, include dirs, and flags (PIO semantics)."""
build = data.get("build", {})
@@ -90,7 +75,7 @@ def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
src_filter = ensure_list(build.get("srcFilter", DEFAULT_BUILD_SRC_FILTER))
# PlatformIO shell-lexes each build.flags entry
raw_flags = join_flag_args(
flag_tokens = join_flag_args(
(
token
for entry in ensure_list(build.get("flags", []))
@@ -101,7 +86,7 @@ def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
lib = ArduinoLibrary(name=name)
include_flags: list[str] = []
for tok in raw_flags:
for tok in flag_tokens:
if tok.startswith("-I"):
include_flags.append(tok[2:])
elif tok.startswith("-L"):
+18 -18
View File
@@ -22,7 +22,6 @@ import shlex
import subprocess
import sys
from esphome.arduino8266.component import join_flag_args
from esphome.components.esp8266 import build_surgery
from esphome.components.esp8266.boards import (
BOARDS,
@@ -40,6 +39,7 @@ from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION
from esphome.core import CORE, EsphomeError
from esphome.framework_helpers import get_project_cxx_compile_flags
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.platformio.library import join_flag_args
# Compile rule per source suffix; keys must cover SRC_FILE_EXTENSIONS so any
# source a library manifest selects has a rule (pinned by a drift test).
@@ -308,23 +308,23 @@ def _project_flags() -> tuple[list[str], list[str], list[Path], list[str]]:
lib_dirs: list[Path] = []
libs: list[str] = []
for flag in flags:
if flag.startswith("-Wl,"):
link_flags.append(flag)
elif flag.startswith(("-L", "-l")):
# Shell-lex only linker entries so forms like "-L /opt/blobs"
# work as they do under PlatformIO. Other entries pass verbatim:
# lexing them would strip the quotes in defines like -DBOARD="...".
for tok in join_flag_args(shlex.split(flag), "esphome"):
if tok.startswith("-L") and len(tok) > 2:
lib_dirs.append(Path(tok[2:]))
elif tok.startswith("-l") and len(tok) > 2:
libs.append(tok[2:])
elif tok.startswith("-Wl,"):
link_flags.append(tok)
else:
compile_flags.append(tok)
else:
compile_flags.append(flag)
# Shell-lex only linker entries so forms like "-L /opt/blobs" work as
# they do under PlatformIO. Other entries pass verbatim: lexing them
# would strip the quotes in defines like -DBOARD="...".
tokens = (
join_flag_args(shlex.split(flag), "esphome")
if flag.startswith(("-L", "-l"))
else [flag]
)
for tok in tokens:
if tok.startswith("-Wl,"):
link_flags.append(tok)
elif tok.startswith("-L"):
lib_dirs.append(Path(tok[2:]))
elif tok.startswith("-l"):
libs.append(tok[2:])
else:
compile_flags.append(tok)
return compile_flags, link_flags, lib_dirs, libs
+8 -16
View File
@@ -27,6 +27,7 @@ from esphome.platformio.library import (
collect_filtered_files,
convert_libraries,
ensure_list,
join_flag_args,
split_list_by_condition,
)
@@ -102,22 +103,13 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
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
# flag and its argument (e.g. "-include cp_custom_alloc.h"); bare
# -I/-L/-l tokens re-glue to their argument ("-I foo" -> "-Ifoo") so the
# prefix classifiers below still route them.
build_flags = join_flag_args(
(token for entry in build_flags for token in shlex.split(entry)),
f"library {component.name}",
)
# List all sources files
build_src_files = collect_filtered_files(
+16 -1
View File
@@ -13,7 +13,7 @@ regardless of which toolchain consumes the result.
"""
from collections import deque
from collections.abc import Callable
from collections.abc import Callable, Iterable
from dataclasses import dataclass, field
import glob
import hashlib
@@ -553,6 +553,21 @@ def _resolve_registry_version(
return owner, name, best["name"], pkgfile["download_url"]
def join_flag_args(tokens: Iterable[str], owner: str) -> list[str]:
"""Join a bare ``-I``/``-L``/``-l`` with its following token (PIO lexing)."""
out: list[str] = []
it = iter(tokens)
for tok in it:
if tok in ("-I", "-L", "-l"):
arg = next(it, None)
if arg is None:
_LOGGER.warning("Ignoring trailing '%s' in %s build flags", tok, owner)
break
tok += arg
out.append(tok)
return out
def normalize_dependencies(dependencies: Any) -> list[dict]:
"""Normalize a library manifest's ``dependencies`` to a list of dicts.