mirror of
https://github.com/esphome/esphome.git
synced 2026-08-27 08:28:30 +00:00
Merge branch 'esp8266-native-library-backend' into esp8266-native-build-spec
This commit is contained in:
+22
-14
@@ -23,6 +23,8 @@ from esphome.platformio.extra_script import apply_extra_script
|
||||
from esphome.platformio.library import (
|
||||
DEFAULT_BUILD_INCLUDE_DIR,
|
||||
DEFAULT_BUILD_SRC_FILTER,
|
||||
ESPHOME_DATA_KEY,
|
||||
ESPHOME_DATA_LINK_FLAGS_KEY,
|
||||
LIBRARY_HEADER_SUFFIXES,
|
||||
SRC_FILE_EXTENSIONS,
|
||||
ConvertedLibrary,
|
||||
@@ -317,20 +319,19 @@ def _external_short_name(name: str) -> str:
|
||||
return short.partition("#")[0].removesuffix(".git")
|
||||
|
||||
|
||||
def _warn_unfulfilled_provides(
|
||||
def _check_unfulfilled_provides(
|
||||
provided_requests: list[str], satisfied: set[str]
|
||||
) -> None:
|
||||
"""Reconcile the provides() promise: every dependency the walk skipped
|
||||
on the backend's word must have been added from the framework tree (or
|
||||
knowingly satisfied by a converted/external library); an unfulfilled
|
||||
promise would surface only as undefined symbols at link."""
|
||||
for name in provided_requests:
|
||||
if name not in satisfied:
|
||||
_LOGGER.warning(
|
||||
"provides() skipped dependency %s but nothing added it; "
|
||||
"the build is missing a library",
|
||||
name,
|
||||
)
|
||||
knowingly satisfied by a converted/external library). An unfulfilled
|
||||
promise can only surface as undefined symbols at link, so it fails
|
||||
here by name like the other can-never-link checks in this module."""
|
||||
if missing := sorted(set(provided_requests) - satisfied):
|
||||
raise EsphomeError(
|
||||
"provides() skipped these dependencies but nothing added them: "
|
||||
f"{', '.join(missing)}; the build is missing libraries"
|
||||
)
|
||||
|
||||
|
||||
def resolve_libraries(
|
||||
@@ -470,11 +471,18 @@ def resolve_libraries(
|
||||
)
|
||||
if isinstance(manifest_name := component.data.get("name"), str):
|
||||
converted_manifest_names.add(manifest_name)
|
||||
converted.append(
|
||||
_library_info(
|
||||
component.get_require_name(), component.source_dir, component.data
|
||||
lib = _library_info(
|
||||
component.get_require_name(), component.source_dir, component.data
|
||||
)
|
||||
# Extra-script LINKFLAGS travel outside build.flags (see
|
||||
# ESPHOME_DATA_LINK_FLAGS_KEY); dropping them would link wrong
|
||||
# with no stated cause
|
||||
lib.link_flags.extend(
|
||||
component.data.get(ESPHOME_DATA_KEY, {}).get(
|
||||
ESPHOME_DATA_LINK_FLAGS_KEY, []
|
||||
)
|
||||
)
|
||||
converted.append(lib)
|
||||
_add_bundled_dependencies(component)
|
||||
|
||||
backend = LibraryBackend(
|
||||
@@ -503,7 +511,7 @@ def resolve_libraries(
|
||||
bundled_names.add(name)
|
||||
bundled.append(_bundled_library(framework_path, name))
|
||||
|
||||
_warn_unfulfilled_provides(
|
||||
_check_unfulfilled_provides(
|
||||
backend.provided_requests,
|
||||
bundled_names | converted_manifest_names | external_short_names,
|
||||
)
|
||||
|
||||
@@ -943,9 +943,11 @@ def _warn_unsatisfied_versionless(
|
||||
continue
|
||||
if dep_name in resolved_manifest_names:
|
||||
# Name-only evidence: any resolved component with this manifest
|
||||
# name counts, not just ones the requester can reach
|
||||
_LOGGER.debug(
|
||||
"Version-less dependency %s of %s satisfied by manifest name only",
|
||||
# name counts, not just ones the requester can reach, so a
|
||||
# coincidental name collision must stay visible
|
||||
_LOGGER.warning(
|
||||
"Version-less dependency %s of %s assumed satisfied by a "
|
||||
"resolved library's manifest name only",
|
||||
dep_name,
|
||||
requester,
|
||||
)
|
||||
|
||||
@@ -716,28 +716,34 @@ def test_bundled_dependency_dict_shorthand_prefers_bundled(tmp_path: Path) -> No
|
||||
assert "Wire" in [lib.name for lib in libs]
|
||||
|
||||
|
||||
def test_unfulfilled_provides_promise_warns(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""The walk records every provides()-skipped dependency; one nothing
|
||||
added must warn instead of surfacing as link errors, while satisfied
|
||||
ones stay silent."""
|
||||
component._warn_unfulfilled_provides(["Wire", "Hash"], {"Hash"})
|
||||
assert "provides() skipped dependency Wire but nothing added it" in caplog.text
|
||||
assert "Hash" not in caplog.text
|
||||
def test_unfulfilled_provides_promise_raises(tmp_path: Path) -> None:
|
||||
"""A provides()-skipped dependency nothing added can only surface as
|
||||
undefined symbols at link, so it fails here by name, deduplicated;
|
||||
satisfied ones pass silently."""
|
||||
with pytest.raises(EsphomeError, match="Wire") as err:
|
||||
component._check_unfulfilled_provides(["Wire", "Wire", "Hash"], {"Hash"})
|
||||
assert str(err.value).count("Wire") == 1
|
||||
assert "Hash" not in str(err.value)
|
||||
component._check_unfulfilled_provides(["Hash"], {"Hash"})
|
||||
|
||||
|
||||
def test_fulfilled_provides_promise_is_silent_end_to_end(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""The normal path: the walk records the skip and the backend adds the
|
||||
bundled copy, so the reconciliation stays quiet."""
|
||||
def test_extra_script_link_flags_reach_the_library(tmp_path: Path) -> None:
|
||||
"""LINKFLAGS captured by an extra script travel outside build.flags and
|
||||
must reach the library's link flags, matching the ESP-IDF backend."""
|
||||
framework = _make_framework(tmp_path)
|
||||
converted = _webserver(tmp_path, {"build": {}, "dependencies": {"Wire": "*"}})
|
||||
converted = _webserver(
|
||||
tmp_path,
|
||||
{
|
||||
"build": {},
|
||||
component.ESPHOME_DATA_KEY: {
|
||||
component.ESPHOME_DATA_LINK_FLAGS_KEY: ["-Wl,--wrap=foo"]
|
||||
},
|
||||
},
|
||||
)
|
||||
with _emitting_converter(converted):
|
||||
libs = _resolve(framework)
|
||||
assert "Wire" in [lib.name for lib in libs]
|
||||
assert "provides() skipped dependency" not in caplog.text
|
||||
(webserver,) = (lib for lib in libs if "ESPAsyncWebServer" in lib.name)
|
||||
assert "-Wl,--wrap=foo" in webserver.link_flags
|
||||
|
||||
|
||||
def test_bundled_dependency_platform_rejection_is_debug(
|
||||
@@ -908,6 +914,8 @@ def test_dict_shorthand_dependency_skips_registry_through_real_converter(
|
||||
names = [lib.name for lib in libs]
|
||||
assert "Wire" in names
|
||||
assert any("locallib" in n.lower() for n in names)
|
||||
# The walk populated provided_requests for the skip; the backend added
|
||||
# the bundled copy, so the reconciliation passed without raising
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user