Match ASPPCOM flag routing, quote manifest link tokens, split the src-dir error, pin the change contract

This commit is contained in:
J. Nick Koston
2026-08-20 16:12:28 -05:00
parent c629e4a94c
commit 252422c706
2 changed files with 63 additions and 5 deletions
+16 -5
View File
@@ -495,6 +495,9 @@ def write_project(paths: InstalledPaths) -> bool:
cache_key="arduino8266",
)
if not src_dir.is_dir():
# Generated project state, not install state: clean-all would not help
raise EsphomeError(f"Generated source directory {src_dir} is missing")
# A missing install directory would otherwise surface as a wall of
# include errors; failing here names the path instead.
include_dirs = [
@@ -505,7 +508,7 @@ def write_project(paths: InstalledPaths) -> bool:
sdk / "lwip2" / "include",
variant_dir,
]
for required in include_dirs:
for required in include_dirs[1:]:
if not required.is_dir():
raise EsphomeError(
f"{_INCOMPLETE_INSTALL}: missing {required}; {_CLEAN_HINT}"
@@ -534,7 +537,14 @@ def write_project(paths: InstalledPaths) -> bool:
+ common
+ get_project_cxx_compile_flags()
)
asflags = _ASFLAGS + defines + includes + project_compile_flags
# PlatformIO's ASPPCOM carries defines and includes but not CCFLAGS,
# so only -D/-I user flags reach assembly there; match it.
asflags = (
_ASFLAGS
+ defines
+ includes
+ [f for f in project_compile_flags if f.startswith(("-D", "-I"))]
)
# build_unflags applies to the framework flag sets too (compile and link),
# as under PlatformIO (a silently ignored ``build_unflags: -Os`` would
@@ -546,7 +556,7 @@ def write_project(paths: InstalledPaths) -> bool:
if esp8266_data[KEY_SCANF_FLOAT]:
link_flags += ["-u", "_scanf_float"]
link_flags += project_link_flags
link_flags += [flag for lib in libraries for flag in lib.link_flags]
link_flags += [_shell_token(flag) for lib in libraries for flag in lib.link_flags]
flash_ld = _active_flash_ld_name(flash_ld_name)
link_flags += ["-T", flash_ld]
@@ -616,7 +626,7 @@ def write_project(paths: InstalledPaths) -> bool:
f"asflags = {' '.join(asflags)}",
f"linkflags = {' '.join(link_flags)}",
f"libdirflags = {' '.join(f'-L{_q(d)}' for d in lib_dirs)}",
f"libflags = {' '.join(f'-l{lib}' for lib in system_libs)}",
f"libflags = {' '.join(_shell_token(f'-l{lib}') for lib in system_libs)}",
"",
]
@@ -625,7 +635,8 @@ def write_project(paths: InstalledPaths) -> bool:
core_exclude |= _CORE_EXCLUDE_WAVEFORM
archives = []
variant_sources = _collect_sources(variant_dir) if variant_dir.is_dir() else []
# variant_dir existence was already enforced with the include dirs
variant_sources = _collect_sources(variant_dir)
if variant_sources:
objs = _ninja_compile_edges(lines, variant_sources, variant_dir, "variant")
lines.append(f"build libFrameworkArduinoVariant.a: ar {' '.join(objs)}")
@@ -676,3 +676,50 @@ def test_ninja_path_escaping() -> None:
"""Build-statement paths and command-line paths escape differently."""
assert arduino8266._e("a b:$c") == "a$ b$:$$c"
assert arduino8266._q("/a b/$x") == '"/a b/$$x"'
def test_write_project_asm_excludes_non_define_user_flags(tmp_path: Path) -> None:
"""The ASPPCOM command under PlatformIO never sees CCFLAGS, so only -D/-I user flags
reach assembly compiles."""
paths = _make_framework(tmp_path)
_set_flags("-DUSER_KNOB=1", "-Wno-volatile")
content = _write_ninja(paths)
asflags = next(line for line in content.splitlines() if line.startswith("asflags"))
assert "-DUSER_KNOB=1" in asflags
assert "-Wno-volatile" not in asflags
cxxflags = next(
line for line in content.splitlines() if line.startswith("cxxflags")
)
assert "-Wno-volatile" in cxxflags
def test_write_project_returns_changed(tmp_path: Path) -> None:
"""The documented contract: True when build.ninja changed, False on an
identical regeneration (pins byte-stable output too)."""
paths = _make_framework(tmp_path)
_set_flags()
src = CORE.relative_src_path()
(src / "esphome" / "components" / "esp8266").mkdir(parents=True, exist_ok=True)
(src / "main.cpp").write_text("")
with (
patch.object(arduino8266, "generate_ld_scripts"),
patch("esphome.arduino.library.resolve_libraries", return_value=[]),
patch("esphome.arduino8266.framework.ccache_path", return_value=None),
):
assert arduino8266.write_project(paths) is True
assert arduino8266.write_project(paths) is False
def test_write_project_missing_src_dir_raises(tmp_path: Path) -> None:
"""A missing generated source tree is its own error, not an install one."""
paths = _make_framework(tmp_path)
_set_flags()
with (
patch.object(arduino8266, "generate_ld_scripts"),
patch("esphome.arduino.library.resolve_libraries", return_value=[]),
patch.object(
arduino8266.CORE, "relative_src_path", return_value=tmp_path / "nope"
),
pytest.raises(EsphomeError, match="source directory"),
):
arduino8266.write_project(paths)