fix(esp32): manifest scope resolves to the DECLARED lib, not first-match
P2.0 first cut took the first-alphabetical lib providing a header and then checked manifest membership. When several installed libs ship the same header (e.g. DHT118266, DHT_sensor_library, servodht11 all have DHT.h), the stray first-match got rejected and the header was dropped even though the declared lib provides it. _find_manifest_library_for_header: when a manifest is supplied, pick the first DECLARED library that provides the header. This both selects the right lib and excludes undeclared ones. No manifest = legacy first-match. Test strengthened with a stray same-header lib that sorts first.
This commit is contained in:
parent
11fabfc67c
commit
1d643797b4
|
|
@ -720,6 +720,23 @@ class ESPIDFCompiler:
|
||||||
props_name = self._parse_library_properties(lib_root).get('name', '')
|
props_name = self._parse_library_properties(lib_root).get('name', '')
|
||||||
return bool(props_name) and self._norm_lib_name(props_name) in allowed_norm
|
return bool(props_name) and self._norm_lib_name(props_name) in allowed_norm
|
||||||
|
|
||||||
|
def _find_manifest_library_for_header(
|
||||||
|
self, header: str, libs_dir: Path, allowed_norm: set[str]
|
||||||
|
) -> Path | None:
|
||||||
|
"""Like _find_library_for_header, but returns the source root of the
|
||||||
|
first library that provides `header` AND is in the project manifest.
|
||||||
|
Returns None when no DECLARED library provides the header — so a stray
|
||||||
|
same-named lib in the shared dir is never picked up."""
|
||||||
|
for lib_dir in sorted(libs_dir.iterdir()):
|
||||||
|
if not lib_dir.is_dir():
|
||||||
|
continue
|
||||||
|
for src_root in (lib_dir, lib_dir / 'src'):
|
||||||
|
if (src_root / header).exists() and self._library_in_manifest(
|
||||||
|
lib_dir, allowed_norm
|
||||||
|
):
|
||||||
|
return src_root
|
||||||
|
return None
|
||||||
|
|
||||||
def _resolve_library_components(
|
def _resolve_library_components(
|
||||||
self,
|
self,
|
||||||
ext_headers: list[str],
|
ext_headers: list[str],
|
||||||
|
|
@ -801,11 +818,22 @@ class ESPIDFCompiler:
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
src_root = (
|
# P2 manifest scope. When a manifest is supplied, resolve the header
|
||||||
self._find_library_for_header(header, arduino_libs)
|
# to the DECLARED library that provides it — not the first-
|
||||||
if arduino_libs and arduino_libs.is_dir()
|
# alphabetical lib in the shared dir. Several installed libs may
|
||||||
else None
|
# ship the same header name (e.g. DHT118266, DHT_sensor_library,
|
||||||
|
# servodht11 all have DHT.h); the legacy first-match would pick a
|
||||||
|
# stray. The manifest both picks the right lib AND excludes
|
||||||
|
# undeclared ones (another user's install, a clash). No manifest =
|
||||||
|
# legacy first-match (unchanged).
|
||||||
|
if not (arduino_libs and arduino_libs.is_dir()):
|
||||||
|
src_root = None
|
||||||
|
elif allowed_norm is not None:
|
||||||
|
src_root = self._find_manifest_library_for_header(
|
||||||
|
header, arduino_libs, allowed_norm
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
src_root = self._find_library_for_header(header, arduino_libs)
|
||||||
|
|
||||||
# Architecture guard. A user lib that resolves the header but
|
# Architecture guard. A user lib that resolves the header but
|
||||||
# whose library.properties declares architectures= without
|
# whose library.properties declares architectures= without
|
||||||
|
|
@ -820,20 +848,6 @@ class ESPIDFCompiler:
|
||||||
)
|
)
|
||||||
src_root = None
|
src_root = None
|
||||||
|
|
||||||
# P2 manifest scope. A user-installed library is merged only if it's
|
|
||||||
# in the project's declared manifest. Anything else from the shared
|
|
||||||
# dir is out of scope — drop it (the header then falls through to the
|
|
||||||
# core check, or to the "not found" path so install-on-missing can
|
|
||||||
# offer to add it to the manifest). Core/bundled libs aren't gated.
|
|
||||||
if src_root is not None and allowed_norm is not None:
|
|
||||||
_lr = src_root.parent if src_root.name == 'src' else src_root
|
|
||||||
if not self._library_in_manifest(_lr, allowed_norm):
|
|
||||||
logger.info(
|
|
||||||
f'[espidf] <{header}> resolves to "{_lr.name}" but it is not in '
|
|
||||||
f'the project library manifest — not merging (scope)'
|
|
||||||
)
|
|
||||||
src_root = None
|
|
||||||
|
|
||||||
# Tracks the "resolved to a core lib that's already compiled into
|
# Tracks the "resolved to a core lib that's already compiled into
|
||||||
# the arduino-esp32 component" case, so we don't fall through to
|
# the arduino-esp32 component" case, so we don't fall through to
|
||||||
# the scary "not found — build may fail" warning below for a
|
# the scary "not found — build may fail" warning below for a
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,11 @@ class TestManifestScope(unittest.TestCase):
|
||||||
"name=DHT sensor library\n")
|
"name=DHT sensor library\n")
|
||||||
_mk(self.ulibs / "DHT_sensor_library" / "DHT.h")
|
_mk(self.ulibs / "DHT_sensor_library" / "DHT.h")
|
||||||
_mk(self.ulibs / "DHT_sensor_library" / "DHT.cpp")
|
_mk(self.ulibs / "DHT_sensor_library" / "DHT.cpp")
|
||||||
|
# A STRAY lib that also ships DHT.h and sorts BEFORE the manifest lib
|
||||||
|
# (real case: DHT118266 sorts before DHT_sensor_library). The manifest
|
||||||
|
# must still resolve DHT.h to the declared lib, not this stray.
|
||||||
|
_mk(self.ulibs / "AAAA_strayDHT" / "DHT.h")
|
||||||
|
_mk(self.ulibs / "AAAA_strayDHT" / "stray_marker.cpp")
|
||||||
# Out-of-manifest lib (e.g. another user's install / a clash).
|
# Out-of-manifest lib (e.g. another user's install / a clash).
|
||||||
_mk(self.ulibs / "RandomOtherLib" / "Foo.h")
|
_mk(self.ulibs / "RandomOtherLib" / "Foo.h")
|
||||||
_mk(self.ulibs / "RandomOtherLib" / "Foo.cpp")
|
_mk(self.ulibs / "RandomOtherLib" / "Foo.cpp")
|
||||||
|
|
@ -126,6 +131,11 @@ class TestManifestScope(unittest.TestCase):
|
||||||
_, hdr2comp = self._resolve(["DHT.h", "Foo.h"], {"DHT sensor library"})
|
_, hdr2comp = self._resolve(["DHT.h", "Foo.h"], {"DHT sensor library"})
|
||||||
self.assertEqual(hdr2comp.get("DHT.h"), "user_libs_all") # declared → merged
|
self.assertEqual(hdr2comp.get("DHT.h"), "user_libs_all") # declared → merged
|
||||||
self.assertNotIn("Foo.h", hdr2comp) # undeclared → dropped
|
self.assertNotIn("Foo.h", hdr2comp) # undeclared → dropped
|
||||||
|
merged = self.out / "user_libs_all"
|
||||||
|
copied = [p.name for p in merged.rglob("*")] if merged.exists() else []
|
||||||
|
# The DECLARED DHT lib was merged, not the stray that sorts first.
|
||||||
|
self.assertIn("DHT.cpp", copied)
|
||||||
|
self.assertNotIn("stray_marker.cpp", copied)
|
||||||
|
|
||||||
def test_none_manifest_is_scan_all(self):
|
def test_none_manifest_is_scan_all(self):
|
||||||
# No manifest → legacy behaviour: both resolve.
|
# No manifest → legacy behaviour: both resolve.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue