Preserve d1_wroom_02's shipped flash layout, refuse plain linker unflags, pin the real spaced-override outcome

This commit is contained in:
J. Nick Koston
2026-08-22 22:29:21 -05:00
parent 4577d69ca9
commit cd3cdde87c
4 changed files with 51 additions and 10 deletions
+18 -1
View File
@@ -456,7 +456,12 @@ def _flash_ld_name(board: str) -> str:
"""
override = _pio_option("board_build.ldscript", "")
if not override:
return ESP8266_LD_SCRIPTS[BOARDS[board][KEY_FLASH_SIZE]][1]
# The same per-board override the PlatformIO path pins (layout
# preservation, see boards.py)
board_data = BOARDS[board]
return board_data.get(
"ldscript", ESP8266_LD_SCRIPTS[board_data[KEY_FLASH_SIZE]][1]
)
if Path(override).name != override:
raise EsphomeError(
f"board_build.ldscript must be a bare script name, got {override!r}"
@@ -893,6 +898,18 @@ def write_project(paths: InstalledPaths, ccache: str | None) -> bool:
_LOGGER.warning(
"build_unflags entries matched no build flag: %s", ", ".join(unmatched)
)
# _LINKFLAGS stores -u and its operand as two tokens; unflagging the
# bare -u would strip all seven and leave the operands as ld "input
# files" with an error pointing nowhere near build_unflags
if plain := sorted(
u
for u in unflags
if u in _PLAIN_LINKER_FLAGS or u.startswith(_PLAIN_LINKER_PREFIXES)
):
raise EsphomeError(
f"build_unflags cannot remove plain linker flag(s) "
f"{', '.join(plain)}; unflag the full -Wl, form or the symbol"
)
cflags, cxxflags, asflags, link_flags = (
[f for f in flags if f not in unflags]
for flags in (cflags, cxxflags, asflags, _LINKFLAGS)
+5 -3
View File
@@ -409,8 +409,8 @@ async def to_code(config: ConfigType) -> None:
)
if config[CONF_BOARD] in BOARDS:
flash_size = BOARDS[config[CONF_BOARD]][KEY_FLASH_SIZE]
ld_scripts = ESP8266_LD_SCRIPTS[flash_size]
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
@@ -419,7 +419,9 @@ async def to_code(config: ConfigType) -> None:
# Old ld script path
ld_script = ld_scripts[0]
else:
ld_script = ld_scripts[1]
# A per-board override preserves a layout the board shipped
# with (see d1_wroom_02 in boards.py)
ld_script = board_data.get("ldscript", ld_scripts[1])
if ld_script is not None:
cg.add_platformio_option("board_build.ldscript", ld_script)
+5
View File
@@ -202,6 +202,11 @@ BOARDS = {
"d1_wroom_02": {
"name": "WeMos D1 ESP-WROOM-02",
"flash_size": FLASH_SIZE_2_MB,
# This board joined BOARDS after shipping with the manifest default
# (64 KB filesystem region); the flash-size default (2m.ld) would
# move _FS_end and with it the preferences sector, wiping existing
# devices' flash-backed state on update.
"ldscript": "eagle.flash.2m64.ld",
},
"d1": {
"name": "WEMOS D1 R1",
+23 -6
View File
@@ -1447,12 +1447,29 @@ def test_flash_ld_name_honors_ldscript_override(tmp_path: Path) -> None:
arduino8266._flash_ld_name("nodemcuv2")
def test_write_project_quotes_spaced_ldscript_override(tmp_path: Path) -> None:
"""An overridden script name re-quotes on the link line like every other
user token (a space would otherwise split into two argv elements)."""
def test_unflagging_a_plain_linker_flag_raises(tmp_path: Path) -> None:
"""build_unflags: -u would strip all seven -u tokens and leave the
operands as ld input files; refuse by name instead."""
paths = _make_framework(tmp_path)
_set_flags()
CORE.build_unflags = {"-u _printf_float"}
with pytest.raises(EsphomeError, match="cannot remove plain linker"):
_write_ninja(paths)
def test_d1_wroom_02_keeps_its_shipped_flash_layout() -> None:
"""The board joined BOARDS late; the flash-size default (2m.ld) would
move _FS_end and the preferences sector on existing devices."""
assert arduino8266._flash_ld_name("d1_wroom_02") == "eagle.flash.2m64.ld"
assert arduino8266._flash_ld_name("nodemcuv2") == "eagle.flash.4m.ld"
def test_write_project_rejects_spaced_ldscript_override(tmp_path: Path) -> None:
"""A spaced override never reaches the link line: generate_ld_scripts
rejects the name first (the -T _shell_token quoting behind it is
defence-in-depth)."""
CORE.platformio_options = {"board_build.ldscript": "my script.ld"}
paths = _make_framework(tmp_path)
_set_flags()
content = _write_ninja(paths)
assert "'my script.ld'" in content or '"my script.ld"' in content
assert "-T my script.ld" not in content
with pytest.raises(EsphomeError, match="Invalid flash linker script name"):
arduino8266.write_project(paths, None)