Merge branch 'esp8266-native-library-converter' into esp8266-native-shared-helpers

This commit is contained in:
J. Nick Koston
2026-08-23 11:36:54 -05:00
4 changed files with 62 additions and 3 deletions
+1
View File
@@ -785,6 +785,7 @@ def _prefetch_idf_tool_archives(
# The installer downloads anything missing itself; never let the
# prefetch become a new way for the install to fail.
_LOGGER.warning("ESP-IDF tool prefetch failed: %s", e)
_LOGGER.debug("Prefetch failure detail", exc_info=True)
def _check_esphome_idf_framework_install(
+20 -3
View File
@@ -114,10 +114,19 @@ class _FakeSConsEnv:
self.result = ExtraScriptResult()
self._warned_methods: set[str] = set()
self._warned_keys: set[str] = set()
self._warned_gets: set[str] = set()
# ----- SCons env API the common scripts use -----
def get(self, key: str, default: str | None = None) -> str | None:
if key not in self._vars and key not in self._warned_gets:
# A script branching on an unmodelled var silently takes the
# default branch; make that diagnosable from a normal build log
self._warned_gets.add(key)
_LOGGER.warning(
"PIO extra-script env.get(%r) is not modelled; returning the default",
key,
)
return self._vars.get(key, default)
def __getitem__(self, key: str) -> str:
@@ -140,6 +149,12 @@ class _FakeSConsEnv:
bucket = getattr(self.result, key.lower())
bucket.extend(items)
# Same keys, same flattened capture; ordering/dedup don't matter since
# the consumer re-orders anyway
Prepend = Append
AppendUnique = Append
PrependUnique = Append
# ----- Everything else is a no-op so unsupported scripts don't crash -----
def __getattr__(self, name: str):
@@ -267,7 +282,7 @@ def captured_as_build_flags(
flags.extend(
f"-L{shlex.quote(_anchored(path))}" for path in _strs(result.libpath, "LIBPATH")
)
flags.extend(f"-l{lib}" for lib in _strs(result.libs, "LIBS"))
flags.extend(f"-l{shlex.quote(lib)}" for lib in _strs(result.libs, "LIBS"))
for define in result.cppdefines:
# SCons also accepts dict/list CPPDEFINES; formatting those blind
# would hand the compiler garbage like -D{'FOO': '1'}
@@ -277,6 +292,8 @@ def captured_as_build_flags(
flags.append(f"-D{define}")
else:
_LOGGER.warning("Ignoring unsupported CPPDEFINES entry %r", define)
flags.extend(_strs(result.linkflags, "LINKFLAGS"))
flags.extend(_strs(result.cppflags, "CPPFLAGS"))
# Each captured entry is one argv token in SCons; quote so the
# lex_build_flags round-trip cannot split a spaced value into two
flags.extend(shlex.quote(f) for f in _strs(result.linkflags, "LINKFLAGS"))
flags.extend(shlex.quote(f) for f in _strs(result.cppflags, "CPPFLAGS"))
return flags
+6
View File
@@ -925,6 +925,12 @@ def _content_lengths(urls: list[str]) -> list[int | None]:
"""Content-Length per URL via HEAD requests; None when unknown."""
import requests
from esphome.happy_eyeballs import ensure_happy_eyeballs
# Same convention as every other network call: without it a broken-IPv6
# network burns the full timeout per HEAD before falling back
ensure_happy_eyeballs()
def head(url: str) -> int | None:
try:
resp = requests.head(url, timeout=10, allow_redirects=True)
@@ -432,6 +432,41 @@ def test_run_extra_script_bad_encoding_is_best_effort(tmp_path, caplog) -> None:
assert "is not UTF-8" in caplog.text
@pytest.mark.parametrize("method", ("Prepend", "AppendUnique", "PrependUnique"))
def test_append_variants_capture_like_append(method: str) -> None:
"""Prepend/AppendUnique/PrependUnique write the captured keys too."""
env = _FakeSConsEnv(
board_mcu="esp8266", pio_env="esphome_esp8266", pio_platform="espressif8266"
)
getattr(env, method)(LIBS=["algobsec"], LIBPATH=["lib"])
assert env.result.libs == ["algobsec"]
assert env.result.libpath == ["lib"]
def test_env_get_unknown_key_warns_once(caplog) -> None:
"""A script branching on an unmodelled env var is diagnosable."""
env = _FakeSConsEnv(
board_mcu="esp8266", pio_env="esphome_esp8266", pio_platform="espressif8266"
)
assert env.get("BOARD") is None
assert env.get("BOARD", "d1") == "d1"
assert env.get("BOARD_MCU") == "esp8266"
assert caplog.text.count("env.get('BOARD') is not modelled") == 1
assert "BOARD_MCU" not in caplog.text
def test_spaced_linkflag_survives_relexing(tmp_path) -> None:
"""A captured argv token with a space stays one token after lexing."""
result = ExtraScriptResult(
linkflags=["-Wl,-T my linker.ld"], cppflags=["-include my hdr.h"]
)
flags = captured_as_build_flags(result, library_dir=tmp_path)
assert lex_build_flags(flags, "test") == [
"-Wl,-T my linker.ld",
"-include my hdr.h",
]
def test_uncaptured_append_key_warns_once(caplog) -> None:
"""A loop of Appends to the same uncaptured key warns once."""