mirror of
https://github.com/esphome/esphome.git
synced 2026-08-26 16:10:29 +00:00
Resolve bare registry libraries at latest version like PlatformIO; tighten review cleanups
This commit is contained in:
@@ -18,7 +18,7 @@ import logging
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
|
||||
from esphome.core import CORE, EsphomeError, Library
|
||||
from esphome.core import CORE, Library
|
||||
from esphome.espidf.extra_script import apply_extra_script
|
||||
from esphome.platformio.library import (
|
||||
DEFAULT_BUILD_INCLUDE_DIR,
|
||||
@@ -140,12 +140,9 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
|
||||
elif (framework_path / "libraries" / library.name).is_dir():
|
||||
bundled.append(_bundled_library(framework_path, library.name))
|
||||
else:
|
||||
# PlatformIO fails on an unresolvable lib_deps entry too; building
|
||||
# without it would only surface as unrelated include/link errors.
|
||||
raise EsphomeError(
|
||||
f"Library {library.name} is not bundled with the Arduino "
|
||||
"framework and has no version or repository to download it from"
|
||||
)
|
||||
# A bare registry name; resolved at the latest version, matching
|
||||
# PlatformIO (a typo fails loudly as a registry lookup error).
|
||||
external.append(library)
|
||||
|
||||
converted: list[ArduinoLibrary] = []
|
||||
bundled_names = {lib.name for lib in bundled}
|
||||
|
||||
@@ -36,6 +36,7 @@ from esphome.framework_helpers import (
|
||||
str_to_lst_of_str,
|
||||
)
|
||||
from esphome.helpers import get_bool_env, get_str_env
|
||||
from esphome.platformio.library import ensure_list
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -135,11 +136,9 @@ def _registry_download(
|
||||
if ver.get("name") != version:
|
||||
continue
|
||||
for file in ver.get("files", []):
|
||||
systems = file.get("system") or "*"
|
||||
# A bare string would make ``in`` a substring test
|
||||
if isinstance(systems, str) and systems != "*":
|
||||
systems = [systems]
|
||||
if systems == "*" or system in systems:
|
||||
# ensure_list: a bare string would make ``in`` a substring test
|
||||
systems = ensure_list(file.get("system") or "*")
|
||||
if "*" in systems or system in systems:
|
||||
return (
|
||||
file["download_url"],
|
||||
(file.get("checksum") or {}).get("sha256"),
|
||||
|
||||
@@ -144,13 +144,11 @@ def _print_size_summary(build_dir: Path, toolchain_path: Path) -> None:
|
||||
try:
|
||||
sections[parts[0]] = int(parts[1])
|
||||
except ValueError:
|
||||
# A confident total built on a dropped section would feed a
|
||||
# wrong number to CI's memory-impact metric; skip the summary.
|
||||
_LOGGER.warning(
|
||||
"Unparsable size output for section %s; skipping the size summary",
|
||||
parts[0],
|
||||
)
|
||||
return
|
||||
_LOGGER.warning("Unparsable size output for section %s", parts[0])
|
||||
if parts[0] in _RAM_SECTIONS or parts[0] in _FLASH_SECTIONS:
|
||||
# A confident total built on a dropped section would feed
|
||||
# a wrong number to CI's memory-impact metric
|
||||
return
|
||||
ram = sum(sections.get(s, 0) for s in _RAM_SECTIONS)
|
||||
flash = sum(sections.get(s, 0) for s in _FLASH_SECTIONS)
|
||||
print(f"RAM: {format_bar(ram, _MAX_RAM_SIZE)}")
|
||||
|
||||
@@ -69,7 +69,9 @@ _CORE_EXCLUDE_WAVEFORM = {
|
||||
"core_esp8266_waveform_phase.cpp",
|
||||
}
|
||||
|
||||
# From platformio-build.py, in its order of precedence (first is the default).
|
||||
# From platformio-build.py. The first entry is the default; with multiple SDK
|
||||
# knobs set (a pathological config) ties break by table order, since
|
||||
# upstream's tie-break depends on define order and is not reproducible here.
|
||||
_NONOSDK_VERSIONS = (
|
||||
("SDK22x_190703", "NONOSDK22x_190703"),
|
||||
("SDK221", "NONOSDK221"),
|
||||
|
||||
@@ -252,11 +252,10 @@ def test_write_project_link_line_and_exclusions(tmp_path: Path) -> None:
|
||||
# -L/-l from esphome build_flags reach the link line, not the compiles
|
||||
assert '-L"/opt/blobs"' in content
|
||||
assert "-luser_blob" in content
|
||||
assert "cflags" not in [
|
||||
line.split(" = ")[0].strip()
|
||||
for line in content.splitlines()
|
||||
if "user_blob" in line
|
||||
]
|
||||
for line in content.splitlines():
|
||||
if line.split(" = ")[0] in ("cflags", "cxxflags", "asflags"):
|
||||
assert "user_blob" not in line
|
||||
assert "/opt/blobs" not in line
|
||||
# System libraries with the selected lwIP variant, in the builder's order
|
||||
assert (
|
||||
"-lhal -lphy -lpp -lnet80211 -llwip2-1460 -lwpa -lcrypto -lmain -lwps "
|
||||
@@ -524,7 +523,8 @@ def test_write_project_missing_framework_dir_raises(tmp_path: Path) -> None:
|
||||
|
||||
|
||||
def test_build_config_nonosdk_precedence() -> None:
|
||||
"""With two SDK knobs set, the first table entry wins (documented order)."""
|
||||
"""With two SDK knobs set (a pathological config), ties break
|
||||
deterministically by table order."""
|
||||
_set_flags(
|
||||
"-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305",
|
||||
"-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221",
|
||||
|
||||
@@ -100,14 +100,15 @@ def test_resolve_libraries_bundled(tmp_path: Path) -> None:
|
||||
assert [lib.name for lib in libs] == ["ESP8266WiFi"]
|
||||
|
||||
|
||||
def test_resolve_libraries_unknown_bare_name_raises(tmp_path: Path) -> None:
|
||||
"""An unresolvable library fails the build by name, as PlatformIO does."""
|
||||
from esphome.core import EsphomeError
|
||||
|
||||
def test_resolve_libraries_bare_registry_name_is_external(tmp_path: Path) -> None:
|
||||
"""A bare name that is not bundled resolves from the registry at the
|
||||
latest version, matching PlatformIO and the documented libraries: key."""
|
||||
framework = _make_framework(tmp_path)
|
||||
_add_library("Typoo", None)
|
||||
with pytest.raises(EsphomeError, match="Typoo"):
|
||||
_add_library("pngle", None)
|
||||
with patch.object(component, "convert_libraries", return_value=[]) as mock_convert:
|
||||
component.resolve_libraries(framework)
|
||||
(libraries, _backend), _ = mock_convert.call_args
|
||||
assert [lib.name for lib in libraries] == ["pngle"]
|
||||
|
||||
|
||||
def _converted(name: str, source_dir: Path, data: dict) -> ConvertedLibrary:
|
||||
|
||||
@@ -223,13 +223,14 @@ def test_run_compile_skips_compdb_when_ninja_unchanged(tmp_path: Path) -> None:
|
||||
run(regenerate_expected=False)
|
||||
|
||||
|
||||
def test_print_size_summary_unparsable_section_skips_summary(
|
||||
def test_print_size_summary_unparsable_section(
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""A section that fails to parse must not produce a confident wrong total."""
|
||||
bad = _SIZE_OUTPUT + ".broken abc 0\n"
|
||||
"""A totals-relevant section that fails to parse must not produce a
|
||||
confident wrong number; an irrelevant one only warns."""
|
||||
bad = _SIZE_OUTPUT.replace(".bss 26504", ".bss abc")
|
||||
with patch.object(
|
||||
toolchain.subprocess,
|
||||
"run",
|
||||
@@ -238,3 +239,17 @@ def test_print_size_summary_unparsable_section_skips_summary(
|
||||
toolchain._print_size_summary(tmp_path, tmp_path / "toolchain")
|
||||
assert capsys.readouterr().out == ""
|
||||
assert "Unparsable size output" in caplog.text
|
||||
|
||||
caplog.clear()
|
||||
harmless = _SIZE_OUTPUT + ".broken abc 0\n"
|
||||
with (
|
||||
patch.object(
|
||||
toolchain.subprocess,
|
||||
"run",
|
||||
return_value=MagicMock(returncode=0, stdout=harmless),
|
||||
),
|
||||
patch.object(toolchain, "_parse_app_size", return_value=1044464),
|
||||
):
|
||||
toolchain._print_size_summary(tmp_path, tmp_path / "toolchain")
|
||||
assert "RAM:" in capsys.readouterr().out
|
||||
assert "Unparsable size output" in caplog.text
|
||||
|
||||
Reference in New Issue
Block a user