Error instead of substituting a state-destroying layout on legacy cores, extract the ld choice, regroup KEY_LDSCRIPT, document segment_length's None

This commit is contained in:
J. Nick Koston
2026-08-24 18:51:10 -05:00
parent 2cd5db4854
commit 69a3ab6fb1
4 changed files with 53 additions and 26 deletions
+26 -22
View File
@@ -277,6 +277,31 @@ def check_rosetta() -> None:
)
def _choose_ld_script(board: str, ver: cv.Version) -> str | None:
"""The flash ld to pin for this board and core, or None for cores
without ld-script support."""
board_data = BOARDS[board]
ld_scripts = ESP8266_LD_SCRIPTS[board_data[KEY_FLASH_SIZE]]
if ver <= cv.Version(2, 3, 0):
# No ld script support
return None
if ver <= cv.Version(2, 4, 2):
# Old ld script path; the modern per-board override names do not
# exist in this core's SDK, so the override cannot be honored.
# Substituting the size default would move _FS_end and the
# preferences sector, wiping flash-backed state on flash.
if KEY_LDSCRIPT in board_data:
raise EsphomeError(
f"Board {board} requires its {board_data[KEY_LDSCRIPT]} "
f"flash layout, which Arduino core {ver} cannot honor; "
"use a core newer than 2.4.2"
)
return ld_scripts[0]
# A per-board override preserves a layout the board shipped with
# (see d1_wroom_02 in boards.py)
return board_ld_script(board_data)
@coroutine_with_priority(CoroPriority.PLATFORM)
async def to_code(config: ConfigType) -> None:
cg.add(esp8266_ns.setup_preferences())
@@ -398,28 +423,7 @@ async def to_code(config: ConfigType) -> None:
)
if config[CONF_BOARD] in BOARDS:
board_data = BOARDS[config[CONF_BOARD]]
ld_scripts = ESP8266_LD_SCRIPTS[board_data[KEY_FLASH_SIZE]]
if ver <= cv.Version(2, 3, 0):
# No ld script support
ld_script = None
elif ver <= cv.Version(2, 4, 2):
# Old ld script path; the modern per-board override names do
# not exist in this core's SDK, so it cannot be honored
if KEY_LDSCRIPT in board_data:
_LOGGER.warning(
"Board %s pins %s, which Arduino core %s cannot honor; "
"using the default flash layout",
config[CONF_BOARD],
board_data[KEY_LDSCRIPT],
ver,
)
ld_script = ld_scripts[0]
else:
# A per-board override preserves a layout the board shipped
# with (see d1_wroom_02 in boards.py)
ld_script = board_ld_script(board_data)
ld_script = _choose_ld_script(config[CONF_BOARD], ver)
if ld_script is not None:
cg.add_platformio_option("board_build.ldscript", ld_script)
+6 -1
View File
@@ -103,7 +103,12 @@ def apply_testing_memory_patches(content: str, segments: Collection[str]) -> str
def segment_length(content: str, segment_name: str) -> int | None:
"""Read a memory segment's length from linker script content."""
"""Read a memory segment's length from linker script content.
Returns None for an absent segment OR an unparsable line; callers must
treat None as "no usable budget" and warn (as the Flash summary does),
never as "no limit".
"""
match = _segment_line_re(segment_name).search(content)
return int(match.group(2), 16) if match else None
+2 -3
View File
@@ -18,6 +18,8 @@ KEY_SERIAL1_REQUIRED = "serial1_required"
# Set for the native (non-PlatformIO) toolchain's build generator
KEY_FLASH_MODE = "flash_mode"
KEY_SCANF_FLOAT = "scanf_float"
# Per-board flash-layout override consumed by board_ld_script()
KEY_LDSCRIPT = "ldscript"
# esp8266 namespace is already defined by arduino, manually prefix esphome
esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266")
@@ -71,6 +73,3 @@ def enable_serial1() -> None:
enable_serial1()
"""
CORE.data.setdefault(KEY_ESP8266, {})[KEY_SERIAL1_REQUIRED] = True
KEY_LDSCRIPT = "ldscript"
@@ -13,3 +13,22 @@ def test_d1_wroom_02_keeps_its_shipped_layout() -> None:
def test_default_boards_use_the_flash_size_layout() -> None:
assert board_ld_script(BOARDS["d1_mini"]) == "eagle.flash.4m.ld"
assert board_ld_script(BOARDS["esp01_1m"]) == "eagle.flash.1m.ld"
def test_choose_ld_script_paths() -> None:
"""Old cores get the size default, overriding boards hard-error there
(a substituted layout would wipe flash-backed state), modern cores
honor the override."""
import pytest
from esphome.components.esp8266 import _choose_ld_script
import esphome.config_validation as cv
from esphome.core import EsphomeError
assert _choose_ld_script("nodemcuv2", cv.Version(2, 3, 0)) is None
assert _choose_ld_script("nodemcuv2", cv.Version(2, 4, 2)) == "eagle.flash.4m.ld"
assert _choose_ld_script("d1_wroom_02", cv.Version(2, 7, 4)) == (
"eagle.flash.2m64.ld"
)
with pytest.raises(EsphomeError, match="cannot honor"):
_choose_ld_script("d1_wroom_02", cv.Version(2, 4, 2))