Honor lib_archive, guard unknown boards by name, quote archive names, fix the asm flag filter

This commit is contained in:
J. Nick Koston
2026-08-20 20:42:23 -05:00
parent 84a10031ee
commit fe5325b007
2 changed files with 60 additions and 5 deletions
+16 -5
View File
@@ -495,8 +495,11 @@ def write_project(paths: InstalledPaths) -> bool:
flag_defines = _flag_defines()
config = _resolve_build_config(flag_defines)
esp8266_data = CORE.data[KEY_ESP8266]
# Board support was validated at config time (_validate_native_toolchain).
board = esp8266_data[KEY_BOARD]
# Config-time validation rejects unsupported boards before this runs;
# guard anyway so a bypassing caller fails by name, not KeyError
if board not in BOARDS or board not in ESP8266_BOARD_BUILD:
raise EsphomeError(f"Board '{board}' is not supported by the native toolchain")
board_build = ESP8266_BOARD_BUILD[board]
flash_ld_name = _flash_ld_name(board)
@@ -557,12 +560,13 @@ def write_project(paths: InstalledPaths) -> bool:
+ get_project_cxx_compile_flags()
)
# PlatformIO's ASPPCOM carries defines and includes but not CCFLAGS,
# so only -D/-I user flags reach assembly there; match it.
# so only -D/-I user flags reach assembly there; match it. The tokens
# are already shell-quoted, so test past a leading quote too.
asflags = (
_ASFLAGS
+ defines
+ includes
+ [f for f in project_compile_flags if f.startswith(("-D", "-I"))]
+ [f for f in project_compile_flags if f.lstrip('"').startswith(("-D", "-I"))]
)
# build_unflags applies to the framework flag sets too (compile and link),
@@ -654,6 +658,7 @@ def write_project(paths: InstalledPaths) -> bool:
core_exclude |= _CORE_EXCLUDE_WAVEFORM
archives = []
direct_objs: list[str] = []
# variant_dir existence was already enforced with the include dirs
variant_sources = _collect_sources(variant_dir)
if variant_sources:
@@ -690,6 +695,12 @@ def write_project(paths: InstalledPaths) -> bool:
f"lib/{lib.name}",
flags=" ".join(_shell_token(f) for f in lib.flags),
)
if not lib.lib_archive:
# libArchive: false / dot_a_linkage=false: hand the objects to
# the linker directly so unreferenced-but-required symbols
# (exception handlers, weak overrides) survive
direct_objs.extend(objs)
continue
archive = f"lib{lib.name}.a"
lines.append(f"build {_e(archive)}: ar {' '.join(objs)}")
archives.append(archive)
@@ -706,10 +717,10 @@ def write_project(paths: InstalledPaths) -> bool:
if CORE.testing_mode:
ld_deps.append(f"ld/{flash_ld}")
lines.append(
f"build firmware.elf: link {' '.join(src_objs)} | "
f"build firmware.elf: link {' '.join(src_objs + direct_objs)} | "
f"{' '.join(_e(a) for a in archives)} {' '.join(_e(d) for d in ld_deps)}"
)
lines.append(f" archives = {' '.join(archives)}")
lines.append(f" archives = {' '.join(_shell_token(a) for a in archives)}")
lines.append("build firmware.bin: elf2bin firmware.elf")
lines.append("build firmware.factory.bin: copy firmware.bin")
lines.append("build firmware.ota.bin: copy firmware.bin")
@@ -772,3 +772,47 @@ def test_generate_ld_scripts_missing_compiler_is_clean(tmp_path: Path) -> None:
_set_flags()
with pytest.raises(EsphomeError, match="Could not run"):
_run_generate_ld_scripts(paths)
def test_write_project_asm_keeps_quoted_defines(tmp_path: Path) -> None:
"""A spaced -D/-I user flag arrives shell-quoted; assembly must still
receive it."""
paths = _make_framework(tmp_path)
_set_flags('-DGREETING="hello world"', "-Wno-volatile")
content = _write_ninja(paths)
asflags = next(line for line in content.splitlines() if line.startswith("asflags"))
assert '"-DGREETING=hello world"' in asflags
assert "-Wno-volatile" not in asflags
def test_write_project_unarchived_library_links_objects(tmp_path: Path) -> None:
"""A libArchive:false library's objects reach the link directly."""
from esphome.arduino.library import ArduinoLibrary
paths = _make_framework(tmp_path)
lib_src = tmp_path / "gdb" / "src"
lib_src.mkdir(parents=True)
(lib_src / "GDBStub.cpp").write_text("")
_set_flags()
lib = ArduinoLibrary(
name="GDBStub",
sources=[lib_src / "GDBStub.cpp"],
include_dirs=[lib_src],
lib_archive=False,
)
content = _write_ninja(paths, libraries=[lib])
assert "libGDBStub.a" not in content
link_line = next(
line for line in content.splitlines() if line.startswith("build firmware.elf")
)
assert "GDBStub.cpp.o" in link_line
def test_write_project_unknown_board_fails_by_name(tmp_path: Path) -> None:
"""A caller bypassing config validation gets the board named, not a
KeyError."""
paths = _make_framework(tmp_path)
_set_flags()
CORE.data[KEY_ESP8266][KEY_BOARD] = "not_a_board"
with pytest.raises(EsphomeError, match="'not_a_board' is not supported"):
_write_ninja(paths)