diff --git a/esphome/arduino/library.py b/esphome/arduino/library.py index a5d4a6f515..5ed263eee6 100644 --- a/esphome/arduino/library.py +++ b/esphome/arduino/library.py @@ -195,7 +195,6 @@ def _collect_lib_sources( name: str, read_path: Path, lib: ArduinoLibrary, - build: dict, src_dir: str, src_filter: list[str], ) -> None: @@ -246,7 +245,7 @@ def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary: name, read_path, lib, lex_build_flags(build.get("flags", []), f"library {name}") ) _resolve_include_dirs(name, read_path, lib, build, src_dir, include_flags) - _collect_lib_sources(name, read_path, lib, build, src_dir, src_filter) + _collect_lib_sources(name, read_path, lib, src_dir, src_filter) return lib @@ -306,12 +305,16 @@ def _external_short_name(name: str) -> str: "owner/Name" and plain names take the last path segment; the "Name=" custom-name form takes the declared name (the URL tail is - a repository path, not a library name). + a repository path, not a library name). Git tails (".git", "#ref") are + stripped like the walk's own URL normalization -- deliberately further + than CORE.add_library's keying, because the comparand here is a manifest + dependency name, never a spec. """ head, sep, tail = name.partition("=") if sep and "://" in tail: return head - return name.rsplit("/", maxsplit=1)[-1] + short = name.rsplit("/", maxsplit=1)[-1] + return short.partition("#")[0].removesuffix(".git") def resolve_libraries( @@ -470,10 +473,13 @@ def resolve_libraries( ) for name in pending_bundled: if name in converted_manifest_names: - # Exact manifest-name evidence: the converted library is this - # library, so the bundled copy would double the archive - _LOGGER.debug( - "Bundled %s suppressed by a converted library's manifest name", + # Manifest-name evidence: the converted library is this library, + # so the bundled copy would double the archive. Warn like the + # external_short_names twin: a coincidental collision would + # otherwise surface only as link errors + _LOGGER.warning( + "Dependency %s is assumed satisfied by a converted library's " + "manifest name; the bundled copy is not added", name, ) continue diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index e3a16c3dd9..c39ddeedef 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -923,6 +923,13 @@ def _warn_unsatisfied_versionless( # A version-less dep's request key is the name itself 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", + dep_name, + requester, + ) continue if ( not dep_owner diff --git a/tests/unit_tests/test_arduino_library.py b/tests/unit_tests/test_arduino_library.py index 9a349c2ea6..f2264db3ac 100644 --- a/tests/unit_tests/test_arduino_library.py +++ b/tests/unit_tests/test_arduino_library.py @@ -984,6 +984,9 @@ def test_transitively_resolved_dependency_does_not_warn( # An "=" without a URL is a registry name, not the custom-name form ("FOO=BAR", "FOO=BAR"), ("https://github.com/x/Wire", "Wire"), + # Git tails are stripped like the walk's URL normalization + ("https://github.com/x/Wire.git", "Wire"), + ("git+https://github.com/x/Wire.git#v1", "Wire"), ], ) def test_external_short_name(spec: str, expected: str) -> None: @@ -991,10 +994,11 @@ def test_external_short_name(spec: str, expected: str) -> None: def test_converted_manifest_name_suppresses_bundled_dependency( - tmp_path: Path, + tmp_path: Path, caplog: pytest.LogCaptureFixture ) -> None: """A name a converted library's manifest provides is not also added - from the framework tree, even when the provider emits later.""" + from the framework tree, even when the provider emits later; the + suppression warns like its external_short_names twin.""" framework = _make_framework(tmp_path) _add_library("ESP32Async/ESPAsyncWebServer", "3.9.6") # Requested under a different short name; only the manifest says "Wire" @@ -1020,6 +1024,7 @@ def test_converted_manifest_name_suppresses_bundled_dependency( "esp32async__ESPAsyncWebServer", "someone__WireLib", ] + assert "Dependency Wire is assumed satisfied by a converted" in caplog.text def test_bundled_library_root_headers_pass_the_probe(tmp_path: Path) -> None: