Require bodies on the MMU segment sizes, split driver link flags, warn on a stuck note

A bare -DMMU_IRAM_SIZE would preprocess to len = 1 and fail far away in
ld (or as a misleading testing-mode message), so MMU_CUSTOM requires a
body on both segment sizes before the shape check. -fuse-ld=/-specs=/
--specs= are driver options with no -Wl, spelling, so they get their
own rejection instead of advice that would make things worse. An
unremovable stale stderr note now warns with the clean hint, since its
consequence is a permanently unstamped cache.
This commit is contained in:
J. Nick Koston
2026-08-23 15:33:37 -05:00
parent db2ea1b935
commit 9cbdfa07cb
2 changed files with 40 additions and 2 deletions
+22 -2
View File
@@ -353,6 +353,12 @@ def _resolve_build_config(defines: dict[str, str]) -> _BuildConfig:
"PIO_FRAMEWORK_ARDUINO_MMU_CUSTOM requires MMU_IRAM_SIZE and "
"MMU_ICACHE_SIZE build flags"
)
for name in _MMU_SEGMENT_SIZE_NAMES:
# A bare -Dname would preprocess to len = 1 and fail far away
if "=" not in defines[name]:
raise EsphomeError(
f"{name} must be a hex literal (e.g. 0x8000), got (no value)"
)
for name, body in defines.items():
if not name.startswith("MMU_") or "=" not in body:
# Valueless flags (MMU_IRAM_HEAP) are legitimate switches
@@ -488,6 +494,12 @@ def _project_flags(
elif tok.startswith("-l"):
libs.append(tok[2:])
else:
if tok.startswith(_PLAIN_DRIVER_LINK_PREFIXES):
# Driver options with no -Wl, spelling; ld would reject them
raise EsphomeError(
f"Link flag {tok} in build_flags is not supported by the "
"native toolchain"
)
if tok in _PLAIN_LINKER_FLAGS or tok.startswith(_PLAIN_LINKER_PREFIXES):
raise EsphomeError(
f"Linker flag {tok} in build_flags is not routed to the "
@@ -511,7 +523,9 @@ _PLAIN_LINKER_FLAGS = (
"-nostdlib",
"-rdynamic",
)
_PLAIN_LINKER_PREFIXES = ("-T", "-Xlinker", "-fuse-ld=", "--specs=")
_PLAIN_LINKER_PREFIXES = ("-T", "-Xlinker")
# Driver options, not ld options: -Wl, has no equivalent for these
_PLAIN_DRIVER_LINK_PREFIXES = ("-fuse-ld=", "--specs=", "-specs=")
def _stat_sig(path: Path) -> str:
@@ -641,7 +655,13 @@ def generate_ld_scripts(
except OSError as err:
# A kept stale note would re-emit an obsolete diagnostic on
# every cache hit; skip the stamp so -E re-derives the truth
_LOGGER.debug("Could not remove %s: %s", stderr_note, err)
_LOGGER.warning(
"Could not remove %s (%s); the linker script will "
"regenerate every build until it is removable; %s",
stderr_note,
err,
_CLEAN_HINT,
)
note_persisted = False
if "SECTIONS" not in result.stdout:
# A degenerate zero-exit run must not be stamped as a good cache
@@ -702,6 +702,24 @@ def test_generate_ld_scripts_unremovable_stale_note_vetoes_the_stamp(
run3.assert_called_once()
@pytest.mark.parametrize("name", ["MMU_IRAM_SIZE", "MMU_ICACHE_SIZE"])
def test_mmu_custom_valueless_segment_size_raises(name: str) -> None:
"""A bare -Dname would preprocess to len = 1 and fail far away in ld."""
other = "MMU_ICACHE_SIZE" if name == "MMU_IRAM_SIZE" else "MMU_IRAM_SIZE"
with pytest.raises(EsphomeError, match=f"{name} must be a hex literal"):
_resolve("-DPIO_FRAMEWORK_ARDUINO_MMU_CUSTOM", f"-D{name}", f"-D{other}=0x8000")
@pytest.mark.parametrize(
"flag", ["-fuse-ld=lld", "--specs=nano.specs", "-specs=nano.specs"]
)
def test_driver_link_flags_rejected_without_wl_advice(flag: str) -> None:
"""No -Wl, spelling exists for these; the message must not suggest one."""
CORE.build_flags = {flag, "-DFOO"}
with pytest.raises(EsphomeError, match="not supported by the native toolchain"):
arduino8266._project_flags(set(), arduino8266._lexed_build_flags())
def test_build_config_mmu_knob_with_raw_mmu_flag_raises() -> None:
"""A variant knob plus a raw MMU_* define would split the compile line
from the linker script; refuse like the no-knob case."""