mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
Merge branch 'esp8266-native-toolchain-plumbing' into esp8266-native-build-infra
This commit is contained in:
@@ -15,6 +15,7 @@ regardless of which toolchain consumes the result.
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
from collections.abc import Callable, Iterable
|
from collections.abc import Callable, Iterable
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
import contextlib
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
import glob
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -103,14 +104,7 @@ class URLSource(Source):
|
|||||||
def __init__(self, url: str):
|
def __init__(self, url: str):
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
def download(
|
def _cache_dir(self, dir_suffix: str, salt: str, namespace: str) -> Path:
|
||||||
self,
|
|
||||||
dir_suffix: str,
|
|
||||||
force: bool = False,
|
|
||||||
salt: str = "",
|
|
||||||
namespace: str = "",
|
|
||||||
progress: Callable[[int], None] | None = None,
|
|
||||||
) -> Path:
|
|
||||||
# Namespace the cache per backend (e.g. pio_components/idf, .../zephyr) so
|
# Namespace the cache per backend (e.g. pio_components/idf, .../zephyr) so
|
||||||
# the build files each backend writes into the library dir can't collide.
|
# the build files each backend writes into the library dir can't collide.
|
||||||
base_dir = Path(CORE.data_dir) / DOMAIN
|
base_dir = Path(CORE.data_dir) / DOMAIN
|
||||||
@@ -120,7 +114,23 @@ class URLSource(Source):
|
|||||||
h.update(self.url.encode())
|
h.update(self.url.encode())
|
||||||
if salt:
|
if salt:
|
||||||
h.update(salt.encode())
|
h.update(salt.encode())
|
||||||
path = base_dir / h.hexdigest()[:8] / dir_suffix
|
return base_dir / h.hexdigest()[:8] / dir_suffix
|
||||||
|
|
||||||
|
def is_cached(self, dir_suffix: str, salt: str = "", namespace: str = "") -> bool:
|
||||||
|
"""Whether a completed extraction already exists for this source."""
|
||||||
|
return (
|
||||||
|
self._cache_dir(dir_suffix, salt, namespace) / ".esphome_extracted"
|
||||||
|
).is_file()
|
||||||
|
|
||||||
|
def download(
|
||||||
|
self,
|
||||||
|
dir_suffix: str,
|
||||||
|
force: bool = False,
|
||||||
|
salt: str = "",
|
||||||
|
namespace: str = "",
|
||||||
|
progress: Callable[[int], None] | None = None,
|
||||||
|
) -> Path:
|
||||||
|
path = self._cache_dir(dir_suffix, salt, namespace)
|
||||||
# Marker file written last to signal a complete extraction. Using a
|
# Marker file written last to signal a complete extraction. Using a
|
||||||
# marker (instead of just `path.is_dir()`) means an interrupted
|
# marker (instead of just `path.is_dir()`) means an interrupted
|
||||||
# extraction is correctly detected and re-run on the next invocation,
|
# extraction is correctly detected and re-run on the next invocation,
|
||||||
@@ -918,6 +928,13 @@ def _prefetch_wave(
|
|||||||
if component.source.url in seen:
|
if component.source.url in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(component.source.url)
|
seen.add(component.source.url)
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
if component.source.is_cached(
|
||||||
|
component.get_sanitized_name(), salt=salt, namespace=namespace
|
||||||
|
):
|
||||||
|
# A completed extraction downloads nothing; a warm build
|
||||||
|
# must stay silent
|
||||||
|
continue
|
||||||
components.append(component)
|
components.append(component)
|
||||||
if len(components) < 2:
|
if len(components) < 2:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -644,6 +644,50 @@ def test_content_lengths_head_requests(monkeypatch: pytest.MonkeyPatch) -> None:
|
|||||||
assert lib._content_lengths(["https://x/a", "https://x/bad"]) == [123, 0]
|
assert lib._content_lengths(["https://x/a", "https://x/bad"]) == [123, 0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_prefetch_wave_cache_probe_failure_still_prefetches(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""The cache probe is best-effort; a failing probe prefetches anyway."""
|
||||||
|
calls: list[str] = []
|
||||||
|
monkeypatch.setattr(
|
||||||
|
ConvertedLibrary,
|
||||||
|
"download",
|
||||||
|
lambda self, **kw: calls.append(self.source.url),
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
URLSource,
|
||||||
|
"is_cached",
|
||||||
|
lambda self, *a, **kw: (_ for _ in ()).throw(RuntimeError("no core")),
|
||||||
|
)
|
||||||
|
wave = [
|
||||||
|
("a", ConvertedLibrary("a", "1.0", URLSource("https://x/a.tar.gz"))),
|
||||||
|
("b", ConvertedLibrary("b", "1.0", URLSource("https://x/b.tar.gz"))),
|
||||||
|
]
|
||||||
|
lib._prefetch_wave(wave, "", "idf")
|
||||||
|
assert sorted(calls) == ["https://x/a.tar.gz", "https://x/b.tar.gz"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_prefetch_wave_warm_cache_is_silent(
|
||||||
|
setup_core, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
|
"""Already-extracted archives download nothing; a warm build must not
|
||||||
|
print a Downloading line or draw a bar."""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
ConvertedLibrary,
|
||||||
|
"download",
|
||||||
|
lambda self, **kw: (_ for _ in ()).throw(AssertionError("downloaded")),
|
||||||
|
)
|
||||||
|
wave = []
|
||||||
|
for name in ("a", "b", "c"):
|
||||||
|
comp = ConvertedLibrary(name, "1.0", URLSource(f"https://x/{name}.tar.gz"))
|
||||||
|
marker_dir = comp.source._cache_dir(comp.get_sanitized_name(), "", "idf")
|
||||||
|
marker_dir.mkdir(parents=True)
|
||||||
|
(marker_dir / ".esphome_extracted").touch()
|
||||||
|
wave.append((name, comp))
|
||||||
|
lib._prefetch_wave(wave, "", "idf")
|
||||||
|
assert "Downloading" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_prefetch_wave_single_archive_skips_the_pool(
|
def test_prefetch_wave_single_archive_skips_the_pool(
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user