mirror of
https://github.com/esphome/esphome.git
synced 2026-08-24 07:06:20 +00:00
Merge branch 'esp8266-native-ninja-emission' into esp8266-arduino-toolchain
This commit is contained in:
@@ -277,6 +277,16 @@ def _resolve_build_config(defines: dict[str, str]) -> _BuildConfig:
|
||||
)
|
||||
|
||||
|
||||
_INCOMPLETE_INSTALL = "Arduino toolchain install is incomplete"
|
||||
_CLEAN_HINT = "run 'esphome clean-all' and retry"
|
||||
|
||||
|
||||
def _active_flash_ld_name(flash_ld_name: str) -> str:
|
||||
"""The flash linker-script filename the link uses (testing mode renames
|
||||
the surgically patched copy)."""
|
||||
return f"testing_{flash_ld_name}" if CORE.testing_mode else flash_ld_name
|
||||
|
||||
|
||||
def _flash_ld_name(board: str) -> str:
|
||||
return ESP8266_LD_SCRIPTS[BOARDS[board][KEY_FLASH_SIZE]][1]
|
||||
|
||||
@@ -358,7 +368,7 @@ def _unflag_tokens() -> set[str]:
|
||||
|
||||
|
||||
def _project_flags(
|
||||
unflags: set[str] | None = None,
|
||||
unflags: set[str],
|
||||
) -> tuple[list[str], list[str], list[Path], list[str]]:
|
||||
"""Split the ESPHome build flags into compile, linker, -L, and -l lists.
|
||||
|
||||
@@ -367,8 +377,6 @@ def _project_flags(
|
||||
``build_unflags`` matches individual tokens (``-Os`` inside ``-Os -g3``).
|
||||
Lexed tokens are re-quoted at emission via ``_shell_token``.
|
||||
"""
|
||||
if unflags is None:
|
||||
unflags = _unflag_tokens()
|
||||
compile_flags: list[str] = []
|
||||
link_flags: list[str] = []
|
||||
lib_dirs: list[Path] = []
|
||||
@@ -477,12 +485,13 @@ def _ninja_compile_edges(
|
||||
for src in sources:
|
||||
rel = src.relative_to(root).as_posix()
|
||||
obj = f"obj/{group}/{rel}.o"
|
||||
lines.append(f"build {_e(obj)}: {_RULE_FOR_SUFFIX[src.suffix]} {_e(src)}")
|
||||
escaped_obj = _e(obj)
|
||||
lines.append(f"build {escaped_obj}: {_RULE_FOR_SUFFIX[src.suffix]} {_e(src)}")
|
||||
if flags:
|
||||
lines.append(f" flags = {flags}")
|
||||
# Escaped once here: the returned paths only ever appear in build
|
||||
# statements (archive/link inputs), which use ninja escaping.
|
||||
objects.append(_e(obj))
|
||||
objects.append(escaped_obj)
|
||||
return objects
|
||||
|
||||
|
||||
@@ -534,8 +543,7 @@ def write_project(paths: dict[str, Path]) -> bool:
|
||||
for required in include_dirs:
|
||||
if not required.is_dir():
|
||||
raise EsphomeError(
|
||||
f"Arduino toolchain install is incomplete: missing {required}; "
|
||||
"run 'esphome clean-all' and retry"
|
||||
f"{_INCOMPLETE_INSTALL}: missing {required}; {_CLEAN_HINT}"
|
||||
)
|
||||
for lib in libraries:
|
||||
include_dirs += lib.include_dirs
|
||||
@@ -575,7 +583,7 @@ def write_project(paths: dict[str, Path]) -> bool:
|
||||
link_flags += ["-u", "_scanf_float"]
|
||||
link_flags += project_link_flags
|
||||
link_flags += [flag for lib in libraries for flag in lib.link_flags]
|
||||
flash_ld = f"testing_{flash_ld_name}" if CORE.testing_mode else flash_ld_name
|
||||
flash_ld = _active_flash_ld_name(flash_ld_name)
|
||||
link_flags += ["-T", flash_ld]
|
||||
|
||||
lib_dirs = [Path("ld"), sdk / "lib", sdk / "ld", sdk / "lib" / config.nonosdk]
|
||||
@@ -666,8 +674,7 @@ def write_project(paths: dict[str, Path]) -> bool:
|
||||
# An empty archive would link into a wall of undefined references
|
||||
# (app_entry, the exception vectors) far from the cause
|
||||
raise EsphomeError(
|
||||
f"Arduino toolchain install is incomplete: no core sources in "
|
||||
f"{core_dir}; run 'esphome clean-all' and retry"
|
||||
f"{_INCOMPLETE_INSTALL}: no core sources in {core_dir}; {_CLEAN_HINT}"
|
||||
)
|
||||
lines.append(f"build libFrameworkArduino.a: ar {' '.join(core_objs)}")
|
||||
archives.append("libFrameworkArduino.a")
|
||||
@@ -694,8 +701,11 @@ def write_project(paths: dict[str, Path]) -> bool:
|
||||
archives.append(archive)
|
||||
|
||||
src_extra = f"-include {_q(src_dir / 'esphome' / 'components' / 'esp8266' / 'throw_stubs.h')}"
|
||||
# One shared variable instead of repeating the flags line on every src
|
||||
# edge (hundreds of edges in a real project)
|
||||
lines.append(f"srcflags = {src_extra}")
|
||||
src_objs = _ninja_compile_edges(
|
||||
lines, _collect_sources(src_dir), src_dir, "src", flags=src_extra
|
||||
lines, _collect_sources(src_dir), src_dir, "src", flags="$srcflags"
|
||||
)
|
||||
|
||||
ld_deps = ["ld/local.eagle.app.v6.common.ld"]
|
||||
@@ -722,9 +732,9 @@ def get_flash_ld_path(build_dir: Path) -> Path:
|
||||
get_framework_path,
|
||||
)
|
||||
|
||||
name = _flash_ld_name(CORE.data[KEY_ESP8266][KEY_BOARD])
|
||||
name = _active_flash_ld_name(_flash_ld_name(CORE.data[KEY_ESP8266][KEY_BOARD]))
|
||||
if CORE.testing_mode:
|
||||
return build_dir / "ld" / f"testing_{name}"
|
||||
return build_dir / "ld" / name
|
||||
version = framework_package_version(CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION])
|
||||
return get_framework_path(version) / "tools" / "sdk" / "ld" / name
|
||||
|
||||
|
||||
@@ -283,12 +283,17 @@ def test_write_project_link_line_and_exclusions(tmp_path: Path) -> None:
|
||||
# Assembly and C sources compile through their own rules
|
||||
assert "cont.S.o: asm" in content
|
||||
assert "abi.c.o: cc" in content
|
||||
# throw_stubs is force-included for ESPHome sources only
|
||||
# throw_stubs is force-included for ESPHome sources only, via one shared
|
||||
# srcflags variable rather than a copy of the flags line per edge
|
||||
src_lines = [line for line in content.splitlines() if "obj/src/" in line]
|
||||
assert any("main.cpp.o: cxx" in line for line in src_lines)
|
||||
assert content.count("throw_stubs.h") == len(
|
||||
[line for line in content.splitlines() if line.startswith(" flags = ")]
|
||||
)
|
||||
assert content.count("throw_stubs.h") == 1
|
||||
assert "srcflags = -include" in content
|
||||
flags_lines = [
|
||||
line for line in content.splitlines() if line.startswith(" flags = ")
|
||||
]
|
||||
assert flags_lines
|
||||
assert all(line == " flags = $srcflags" for line in flags_lines)
|
||||
|
||||
|
||||
def test_write_project_scanf_float_and_waveform_kept(tmp_path: Path) -> None:
|
||||
@@ -579,7 +584,9 @@ def test_project_flags_trailing_bare_linker_flag_warns(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
_set_flags("-l")
|
||||
compile_flags, link_flags, lib_dirs, libs = arduino8266._project_flags()
|
||||
compile_flags, link_flags, lib_dirs, libs = arduino8266._project_flags(
|
||||
arduino8266._unflag_tokens()
|
||||
)
|
||||
assert "Ignoring trailing '-l'" in caplog.text
|
||||
assert not libs
|
||||
assert not lib_dirs
|
||||
@@ -589,7 +596,9 @@ def test_project_flags_trailing_bare_linker_flag_warns(
|
||||
|
||||
def test_project_flags_lexed_entry_scatters_non_linker_tokens() -> None:
|
||||
_set_flags("-L /d -Wl,-Map=m stray")
|
||||
compile_flags, link_flags, lib_dirs, libs = arduino8266._project_flags()
|
||||
compile_flags, link_flags, lib_dirs, libs = arduino8266._project_flags(
|
||||
arduino8266._unflag_tokens()
|
||||
)
|
||||
assert lib_dirs == [Path("/d")]
|
||||
assert link_flags == ["-Wl,-Map=m"]
|
||||
assert "stray" in compile_flags
|
||||
@@ -609,7 +618,9 @@ def test_flag_defines_lexes_multi_token_entries() -> None:
|
||||
def test_project_flags_lexes_every_entry() -> None:
|
||||
"""A linker flag anywhere in an entry reaches the link line (PIO parity)."""
|
||||
_set_flags("-DFOO=1 -lbar")
|
||||
compile_flags, _link, _dirs, libs = arduino8266._project_flags()
|
||||
compile_flags, _link, _dirs, libs = arduino8266._project_flags(
|
||||
arduino8266._unflag_tokens()
|
||||
)
|
||||
assert libs == ["bar"]
|
||||
assert "-DFOO=1" in compile_flags
|
||||
|
||||
@@ -618,7 +629,9 @@ def test_project_flags_unflags_match_tokens() -> None:
|
||||
"""build_unflags removes a token embedded in a multi-token entry."""
|
||||
_set_flags("-Os -g3")
|
||||
CORE.build_unflags = {"-Os"}
|
||||
compile_flags, _link, _dirs, _libs = arduino8266._project_flags()
|
||||
compile_flags, _link, _dirs, _libs = arduino8266._project_flags(
|
||||
arduino8266._unflag_tokens()
|
||||
)
|
||||
assert "-g3" in compile_flags
|
||||
assert "-Os" not in compile_flags
|
||||
|
||||
@@ -626,7 +639,9 @@ def test_project_flags_unflags_match_tokens() -> None:
|
||||
def test_project_flags_requotes_lexed_defines() -> None:
|
||||
"""A quoted spaced value stays one compiler argument after lex/emit."""
|
||||
_set_flags('-DGREETING="hello world"')
|
||||
compile_flags, _link, _dirs, _libs = arduino8266._project_flags()
|
||||
compile_flags, _link, _dirs, _libs = arduino8266._project_flags(
|
||||
arduino8266._unflag_tokens()
|
||||
)
|
||||
# shlex folds the quotes (as PIO's ParseFlags does); _shell_token
|
||||
# re-quotes the spaced token so the shell passes one argv element
|
||||
assert compile_flags == ['"-DGREETING=hello world"']
|
||||
@@ -652,16 +667,8 @@ def test_write_project_empty_core_raises(tmp_path: Path) -> None:
|
||||
for f in core.iterdir():
|
||||
f.unlink()
|
||||
_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.arduino8266.component.resolve_libraries", return_value=[]),
|
||||
patch("esphome.arduino8266.framework.ccache_path", return_value=None),
|
||||
pytest.raises(EsphomeError, match="no core sources"),
|
||||
):
|
||||
arduino8266.write_project(paths)
|
||||
with pytest.raises(EsphomeError, match="no core sources"):
|
||||
_write_ninja(paths)
|
||||
|
||||
|
||||
def test_flag_defines_joins_spaced_define() -> None:
|
||||
|
||||
Reference in New Issue
Block a user