Move the Arduino library backend to esphome/arduino and close_fds the ar shim

This commit is contained in:
J. Nick Koston
2026-08-20 15:26:17 -05:00
parent 2508899cad
commit ca47309a36
4 changed files with 65 additions and 23 deletions
View File
@@ -1,10 +1,12 @@
"""Arduino ESP8266 backend for the shared PlatformIO library converter.
"""Arduino-core backend for the shared PlatformIO library converter.
Turns the libraries registered via ``cg.add_library()`` into build inputs for
the ninja generator. Bare names that exist under the framework's bundled
a native Arduino build. Bare names that exist under the framework's bundled
``libraries/`` directory (ESP8266WiFi, Wire, SPI, ...) are read straight from
the framework tree; everything else goes through the shared
resolution/download pipeline in ``esphome.platformio.library``.
resolution/download pipeline in ``esphome.platformio.library``. Nothing here
is core-specific: the caller names the PlatformIO platform, MCU, and cache
key of the Arduino core it builds.
Mirrors PlatformIO's ``lib_ldf_mode=off`` behavior: each library builds into
its own static archive and every library's include dir joins one global
@@ -40,8 +42,6 @@ from esphome.platformio.library import (
_LOGGER = logging.getLogger(__name__)
ESP8266_PLATFORM = "espressif8266"
@dataclass
class ArduinoLibrary:
@@ -145,8 +145,15 @@ def _bundled_library(framework_path: Path, name: str) -> ArduinoLibrary:
return _library_info(name, lib_dir, {"name": name, **data})
def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
"""Resolve every ``cg.add_library()`` entry into an :class:`ArduinoLibrary`."""
def resolve_libraries(
framework_path: Path, *, pio_platform: str, board_mcu: str, cache_key: str
) -> list[ArduinoLibrary]:
"""Resolve every ``cg.add_library()`` entry into an :class:`ArduinoLibrary`.
``pio_platform``/``board_mcu`` filter manifests the way PlatformIO would
for that core (e.g. ``espressif8266``/``esp8266``); ``cache_key`` keys the
shared converter's download cache.
"""
bundled: list[ArduinoLibrary] = []
external: list[Library] = []
# PlatformIO's lib_ignore covers framework-bundled libraries too; the
@@ -200,7 +207,7 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
)
continue
try:
check_library_data(dep, ESP8266_PLATFORM, "arduino")
check_library_data(dep, pio_platform, "arduino")
except InvalidLibrary as err:
# check_library_data's only raise is the platform filter, and
# rejecting another platform's dependency of a cross-platform
@@ -212,9 +219,7 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
bundled.append(_bundled_library(framework_path, name))
def _emit(component: ConvertedLibrary) -> None:
apply_extra_script(
component, board_mcu="esp8266", pio_platform=ESP8266_PLATFORM
)
apply_extra_script(component, board_mcu=board_mcu, pio_platform=pio_platform)
converted.append(
_library_info(
component.get_require_name(), component.source_dir, component.data
@@ -226,10 +231,10 @@ def resolve_libraries(framework_path: Path) -> list[ArduinoLibrary]:
convert_libraries(
external,
LibraryBackend(
platform=ESP8266_PLATFORM,
platform=pio_platform,
framework="arduino",
emit=_emit,
cache_key="arduino8266",
cache_key=cache_key,
),
)
+3 -1
View File
@@ -25,7 +25,9 @@ def main() -> int:
# treats backslashes in response files as escapes, corrupting Windows
# paths ("sub\a.o" -> "suba.o").
objects = Path(rspfile).read_text(encoding="utf-8").split()
return subprocess.run([ar, "rc", archive, *objects], check=False).returncode
return subprocess.run(
[ar, "rc", archive, *objects], check=False, close_fds=False
).returncode
if mode == "copy":
src, dst = sys.argv[2:4]
shutil.copyfile(src, dst)
@@ -1,4 +1,4 @@
"""Tests for esphome.arduino8266.component (library resolution)."""
"""Tests for esphome.arduino.library (Arduino-core library resolution)."""
from __future__ import annotations
@@ -8,7 +8,7 @@ from unittest.mock import patch
import pytest
from esphome.arduino8266 import component
from esphome.arduino import library as component
from esphome.const import KEY_CORE, KEY_TARGET_PLATFORM, PLATFORM_ESP8266
from esphome.core import CORE, EsphomeError, Library
from esphome.platformio.library import ConvertedLibrary, LibraryBackend
@@ -149,7 +149,12 @@ def test_library_info_no_src_dir(tmp_path: Path) -> None:
def test_resolve_libraries_bundled(tmp_path: Path) -> None:
framework = _make_framework(tmp_path)
_add_library("ESP8266WiFi", None)
libs = component.resolve_libraries(framework)
libs = component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
assert [lib.name for lib in libs] == ["ESP8266WiFi"]
@@ -159,7 +164,12 @@ def test_resolve_libraries_bare_registry_name_is_external(tmp_path: Path) -> Non
framework = _make_framework(tmp_path)
_add_library("pngle", None)
with patch.object(component, "convert_libraries", return_value=[]) as mock_convert:
component.resolve_libraries(framework)
component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
(libraries, _backend), _ = mock_convert.call_args
assert [lib.name for lib in libraries] == ["pngle"]
@@ -197,7 +207,12 @@ def test_resolve_libraries_external_and_bundled_deps(tmp_path: Path) -> None:
)
with _emitting_converter(converted) as mock_extra:
libs = component.resolve_libraries(framework)
libs = component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
mock_extra.assert_called_once_with(
converted, board_mcu="esp8266", pio_platform="espressif8266"
@@ -220,7 +235,12 @@ def test_resolve_libraries_bundled_dep_already_present(tmp_path: Path) -> None:
)
with _emitting_converter(converted):
libs = component.resolve_libraries(framework)
libs = component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
# Wire appears once (from the explicit registration), not twice
assert [lib.name for lib in libs] == ["Wire", "some__External"]
@@ -233,7 +253,12 @@ def test_resolve_libraries_versioned_bare_name_is_external(tmp_path: Path) -> No
_add_library("pngle", "1.1.0")
with patch.object(component, "convert_libraries", return_value=[]) as mock_convert:
component.resolve_libraries(framework)
component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
(libraries, _backend), _ = mock_convert.call_args
assert [lib.name for lib in libraries] == ["pngle"]
@@ -283,7 +308,12 @@ def test_resolve_libraries_lib_ignore_covers_bundled(tmp_path: Path) -> None:
_add_library("ESP8266WiFi", None)
_add_library("Wire", None)
CORE.platformio_options = {"lib_ignore": ["Wire"]}
libs = component.resolve_libraries(framework)
libs = component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
assert [lib.name for lib in libs] == ["ESP8266WiFi"]
@@ -301,7 +331,12 @@ def test_resolve_libraries_lib_ignore_covers_bundled_dependencies(
)
with _emitting_converter(converted):
libs = component.resolve_libraries(framework)
libs = component.resolve_libraries(
framework,
pio_platform="espressif8266",
board_mcu="esp8266",
cache_key="arduino8266",
)
assert [lib.name for lib in libs] == ["some__External"]