mirror of
https://github.com/esphome/esphome.git
synced 2026-10-03 17:55:57 +00:00
Surface malformed build flag entries as clean errors
This commit is contained in:
@@ -16,7 +16,6 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
|
||||
from esphome.core import CORE, Library
|
||||
from esphome.espidf.extra_script import apply_extra_script
|
||||
@@ -36,6 +35,7 @@ from esphome.platformio.library import (
|
||||
lib_ignore_set,
|
||||
normalize_dependencies,
|
||||
parse_library_properties,
|
||||
split_flag_entry,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -79,7 +79,7 @@ def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
|
||||
(
|
||||
token
|
||||
for entry in ensure_list(build.get("flags", []))
|
||||
for token in shlex.split(entry)
|
||||
for token in split_flag_entry(entry, f"library {name}")
|
||||
),
|
||||
f"library {name}",
|
||||
)
|
||||
|
||||
@@ -19,7 +19,6 @@ import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -40,7 +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
|
||||
from esphome.platformio.library import join_flag_args, split_flag_entry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -179,7 +178,7 @@ def _flag_defines() -> dict[str, str]:
|
||||
# Shell-lex multi-token entries the way PlatformIO does, so a knob
|
||||
# in "-DKNOB -DOTHER" is still detected; single tokens pass verbatim
|
||||
# to keep any quoting in their bodies intact.
|
||||
for tok in shlex.split(flag) if " " in flag else (flag,):
|
||||
for tok in split_flag_entry(flag, "esphome") if " " in flag else (flag,):
|
||||
if tok.startswith("-D"):
|
||||
body = tok[2:]
|
||||
defines[body.split("=", 1)[0]] = body
|
||||
@@ -319,7 +318,7 @@ def _project_flags() -> tuple[list[str], list[str], list[Path], list[str]]:
|
||||
# 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")
|
||||
join_flag_args(split_flag_entry(flag, "esphome"), "esphome")
|
||||
if flag.startswith(("-L", "-l"))
|
||||
else [flag]
|
||||
)
|
||||
|
||||
@@ -28,6 +28,7 @@ from esphome.platformio.library import (
|
||||
convert_libraries,
|
||||
ensure_list,
|
||||
join_flag_args,
|
||||
split_flag_entry,
|
||||
split_list_by_condition,
|
||||
)
|
||||
|
||||
@@ -62,10 +63,6 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
|
||||
Returns:
|
||||
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:
|
||||
# In CMakeLists.txt, backslashes need to be escaped
|
||||
@@ -107,7 +104,11 @@ def generate_cmakelists_txt(component: IDFComponent) -> str:
|
||||
# -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)),
|
||||
(
|
||||
token
|
||||
for entry in build_flags
|
||||
for token in split_flag_entry(entry, f"library {component.name}")
|
||||
),
|
||||
f"library {component.name}",
|
||||
)
|
||||
|
||||
|
||||
@@ -553,6 +553,16 @@ def _resolve_registry_version(
|
||||
return owner, name, best["name"], pkgfile["download_url"]
|
||||
|
||||
|
||||
def split_flag_entry(entry: str, owner: str) -> list[str]:
|
||||
"""``shlex.split`` with a clean error naming the offending flags entry."""
|
||||
import shlex
|
||||
|
||||
try:
|
||||
return shlex.split(entry)
|
||||
except ValueError as err:
|
||||
raise EsphomeError(f"Malformed build flag {entry!r} in {owner}: {err}") from err
|
||||
|
||||
|
||||
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] = []
|
||||
|
||||
@@ -531,3 +531,12 @@ def test_convert_libraries_skips_incompatible_dependency(tmp_path, monkeypatch):
|
||||
top = convert_libraries([Library("esphome/A", "1.0.0", None)], _backend())
|
||||
|
||||
assert top[0].dependencies == []
|
||||
|
||||
|
||||
def test_split_flag_entry_unbalanced_quote_is_clean() -> None:
|
||||
"""A malformed flags entry raises EsphomeError, not a raw ValueError."""
|
||||
from esphome.platformio.library import split_flag_entry
|
||||
|
||||
assert split_flag_entry('-DX="a b"', "library x") == ["-DX=a b"]
|
||||
with pytest.raises(EsphomeError, match=r"Malformed build flag.*library x"):
|
||||
split_flag_entry('-DX="unclosed', "library x")
|
||||
|
||||
Reference in New Issue
Block a user