Merge branch 'esp8266-native-build-spec' into esp8266-native-ninja-emission

This commit is contained in:
J. Nick Koston
2026-08-20 18:28:28 -05:00
2 changed files with 61 additions and 4 deletions
+16 -4
View File
@@ -131,6 +131,9 @@ _MMU_VARIANTS = (
)
_MMU_DEFAULT = ("MMU_IRAM_SIZE=0x8000", "MMU_ICACHE_SIZE=0x8000")
# Upstream's CXXFLAGS (-fno-rtti, the -std level, -f(no-)exceptions) and the
# trailing stdc++/m/c/gcc system libs are composed at emission
# (write_project) from CORE.cpp_standard and _BuildConfig.exceptions.
_ASFLAGS = ["-mlongcalls", "-mtext-section-literals"]
_CFLAGS = [
"-std=gnu17",
@@ -208,7 +211,10 @@ class _BuildConfig:
def _flag_defines() -> dict[str, str]:
"""Map define name -> full ``NAME[=VALUE]`` for every -D build flag."""
defines: dict[str, str] = {}
for flag in CORE.build_flags:
# Sorted so duplicate defines resolve the same way every run: the
# winner feeds the linker-script preprocessor line, which is also the
# cache stamp
for flag in sorted(CORE.build_flags):
# Shell-lex every entry the way PlatformIO's ParseFlags does, so a
# knob in "-DKNOB -DOTHER", a spaced "-D KNOB", and quoted bodies all
# read identically to _project_flags (and the compile line).
@@ -414,9 +420,15 @@ def generate_ld_scripts(
and stamp.is_file()
and stamp.read_text(encoding="utf-8") == stamp_content
):
result = subprocess.run(
cmd, capture_output=True, text=True, check=False, close_fds=False
)
try:
result = subprocess.run(
cmd, capture_output=True, text=True, check=False, close_fds=False
)
except OSError as err:
# A half-extracted or half-deleted toolchain cache reaches here
raise EsphomeError(
f"Could not run {gcc}: {err}; run 'esphome clean-all' and retry"
) from err
if result.returncode != 0:
raise EsphomeError(f"Generating the linker script failed:\n{result.stderr}")
content = build_surgery.relocate_ratetable(result.stdout)
@@ -727,3 +727,48 @@ def test_flag_defines_lexes_quoted_single_tokens() -> None:
"""A quoted single-token define reads the same as on the compile line."""
_set_flags('-DMMU_SEC_HEAP="0x40108000"')
assert _flag_defines()["MMU_SEC_HEAP"] == "MMU_SEC_HEAP=0x40108000"
def test_flag_defines_duplicate_defines_resolve_deterministically() -> None:
"""Duplicate conflicting defines pick the same winner every run (sorted
iteration, last writer wins), independent of the set's hash seed."""
_set_flags("-DMMU_IRAM_SIZE=0x8000", "-DMMU_IRAM_SIZE=0xC000")
assert _flag_defines()["MMU_IRAM_SIZE"] == "MMU_IRAM_SIZE=0xC000"
def test_flag_tables_match_platformio_builder() -> None:
"""The transliterated flag lists pinned verbatim, like the define set:
a drift lands as a test failure, not a binary-size regression."""
assert arduino8266._ASFLAGS == ["-mlongcalls", "-mtext-section-literals"]
assert arduino8266._CFLAGS == [
"-std=gnu17",
"-Wpointer-arith",
"-Wno-implicit-function-declaration",
"-Wl,-EL",
"-fno-inline-functions",
"-nostdlib",
]
assert arduino8266._CCFLAGS[:6] == [
"-Os",
"-mlongcalls",
"-mtext-section-literals",
"-falign-functions=4",
"-U__STRICT_ANSI__",
"-ffunction-sections",
]
assert arduino8266._LINKFLAGS[:5] == [
"-Os",
"-nostdlib",
"-Wl,--no-check-sections",
"-Wl,-static",
"-Wl,--gc-sections",
]
def test_generate_ld_scripts_missing_compiler_is_clean(tmp_path: Path) -> None:
"""A half-deleted toolchain cache fails with an ESPHome error naming the
binary, not a FileNotFoundError traceback."""
paths = _make_framework(tmp_path)
_set_flags()
with pytest.raises(EsphomeError, match="Could not run"):
_run_generate_ld_scripts(paths)