mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
Adopt the shared toolchain check, dedupe the native-module dispatch, and complete the trigger set
This commit is contained in:
+21
-12
@@ -813,7 +813,7 @@ def write_cpp_file() -> int:
|
||||
from esphome.build_gen import espidf
|
||||
|
||||
espidf.write_project()
|
||||
elif CORE.using_toolchain_arduino and CORE.is_esp8266:
|
||||
elif CORE.using_toolchain_arduino:
|
||||
# The ninja project is generated at compile time by
|
||||
# esphome.arduino8266.toolchain (it needs the downloaded framework).
|
||||
pass
|
||||
@@ -968,7 +968,7 @@ def upload_using_esptool(
|
||||
flash_images = [
|
||||
FlashImage(path=toolchain.get_factory_firmware_path(), offset="0x0")
|
||||
]
|
||||
elif CORE.using_toolchain_arduino and CORE.is_esp8266:
|
||||
elif CORE.using_toolchain_arduino:
|
||||
# The native backend writes PlatformIO-compatible output paths, so the
|
||||
# shared property already points at the right file.
|
||||
flash_images = [FlashImage(path=CORE.firmware_bin, offset="0x0")]
|
||||
@@ -1917,14 +1917,27 @@ def command_update_all(args: ArgsProtocol) -> int | None:
|
||||
return run_multiple_configs(files, build_command)
|
||||
|
||||
|
||||
def _native_toolchain_module():
|
||||
"""The native build backend module for the resolved toolchain, if any.
|
||||
|
||||
Platform toolchain validation rejects values a platform cannot serve, so
|
||||
using_toolchain_arduino by itself implies the native ESP8266 build.
|
||||
"""
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
from esphome.espidf import toolchain
|
||||
|
||||
return toolchain
|
||||
if CORE.using_toolchain_arduino:
|
||||
from esphome.arduino8266 import toolchain
|
||||
|
||||
return toolchain
|
||||
return None
|
||||
|
||||
|
||||
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
|
||||
import json
|
||||
|
||||
native_toolchain = None
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
from esphome.espidf import toolchain as native_toolchain
|
||||
elif CORE.using_toolchain_arduino and CORE.is_esp8266:
|
||||
from esphome.arduino8266 import toolchain as native_toolchain
|
||||
native_toolchain = _native_toolchain_module()
|
||||
|
||||
if native_toolchain is not None:
|
||||
# Native toolchains derive idedata from the build's
|
||||
@@ -1979,11 +1992,7 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
|
||||
|
||||
# Get idedata for analysis
|
||||
idedata = None
|
||||
native_toolchain = None
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
from esphome.espidf import toolchain as native_toolchain
|
||||
elif CORE.using_toolchain_arduino and CORE.is_esp8266:
|
||||
from esphome.arduino8266 import toolchain as native_toolchain
|
||||
native_toolchain = _native_toolchain_module()
|
||||
|
||||
if native_toolchain is not None:
|
||||
objdump_path = str(native_toolchain.get_objdump_path())
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
@@ -15,7 +14,7 @@ from esphome.const import (
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.helpers import write_file_if_changed
|
||||
from esphome.helpers import IS_WINDOWS, write_file_if_changed
|
||||
from esphome.types import ConfigType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -58,7 +57,7 @@ def get_elf_path() -> Path:
|
||||
|
||||
|
||||
# Windows binutils carry the executable suffix; is_file() checks need it
|
||||
_EXE_SUFFIX = ".exe" if os.name == "nt" else ""
|
||||
_EXE_SUFFIX = ".exe" if IS_WINDOWS else ""
|
||||
|
||||
|
||||
def _toolchain_tool(name: str) -> Path:
|
||||
|
||||
@@ -118,11 +118,7 @@ def _resolve_toolchain(config: ConfigType) -> ConfigType:
|
||||
# Resolve toolchain: CLI (already on CORE.toolchain) > YAML > default.
|
||||
if CORE.toolchain is None:
|
||||
CORE.toolchain = config.get(CONF_TOOLCHAIN, Toolchain.PLATFORMIO)
|
||||
if CORE.toolchain not in (Toolchain.PLATFORMIO, Toolchain.ARDUINO):
|
||||
raise cv.Invalid(
|
||||
f"Unsupported toolchain '{CORE.toolchain.value}' for ESP8266. "
|
||||
"Supported toolchains are 'platformio' and 'arduino'."
|
||||
)
|
||||
cv.check_supported_toolchain("ESP8266", (Toolchain.PLATFORMIO, Toolchain.ARDUINO))
|
||||
return config
|
||||
|
||||
|
||||
@@ -152,6 +148,8 @@ def _validate_native_toolchain(config: ConfigType) -> ConfigType:
|
||||
"'toolchain: arduino' does not support a custom framework source; "
|
||||
"use 'toolchain: platformio'"
|
||||
)
|
||||
# BOARDS is a subset of ESP8266_BOARD_BUILD today; the second clause is
|
||||
# a drift guard for the independently regenerated tables
|
||||
if (
|
||||
config[CONF_BOARD] not in BOARDS
|
||||
or config[CONF_BOARD] not in ESP8266_BOARD_BUILD
|
||||
@@ -617,10 +615,9 @@ def _decode_pc(config, addr):
|
||||
|
||||
addr2line = native_toolchain.get_addr2line_path()
|
||||
elf = native_toolchain.get_elf_path()
|
||||
if not addr2line.is_file() or not elf.is_file():
|
||||
_warn_missing_decode_tool(
|
||||
str(addr2line if not addr2line.is_file() else elf)
|
||||
)
|
||||
missing = addr2line if not addr2line.is_file() else elf
|
||||
if not missing.is_file():
|
||||
_warn_missing_decode_tool(str(missing))
|
||||
return
|
||||
addr2line, elf = str(addr2line), str(elf)
|
||||
else:
|
||||
|
||||
@@ -642,8 +642,8 @@ ESP8266_NATIVE_TEST_COMPONENTS = frozenset(
|
||||
)
|
||||
|
||||
# Infrastructure whose changes always trigger the native ESP8266 compile
|
||||
# test. esphome/espidf/ is included because the backend shares its idedata,
|
||||
# extra-script, and size-summary helpers.
|
||||
# test. esphome/build_helpers/ holds the idedata and size-summary helpers
|
||||
# the backend shares with the native ESP-IDF build.
|
||||
ESP8266_NATIVE_TRIGGER_PATH_PREFIXES = (
|
||||
"esphome/arduino8266/",
|
||||
"esphome/build_helpers/",
|
||||
@@ -651,6 +651,8 @@ ESP8266_NATIVE_TRIGGER_PATH_PREFIXES = (
|
||||
ESP8266_NATIVE_TRIGGER_FILES = frozenset(
|
||||
{
|
||||
"esphome/build_gen/arduino8266.py",
|
||||
"esphome/build_gen/build_tool.py",
|
||||
"esphome/platformio/extra_script.py",
|
||||
"esphome/components/esp8266/build_surgery.py",
|
||||
"esphome/components/esp8266/boards.py",
|
||||
"esphome/platformio/library.py",
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.esp8266 import (
|
||||
ARDUINO_FRAMEWORK_SCHEMA,
|
||||
_resolve_toolchain,
|
||||
_validate_native_toolchain,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
@@ -16,6 +18,7 @@ from esphome.const import (
|
||||
CONF_FRAMEWORK,
|
||||
CONF_PLATFORM_VERSION,
|
||||
CONF_SOURCE,
|
||||
CONF_TOOLCHAIN,
|
||||
CONF_VERSION,
|
||||
Toolchain,
|
||||
)
|
||||
@@ -101,9 +104,6 @@ def test_unsupported_board_rejected() -> None:
|
||||
|
||||
def test_yaml_toolchain_key_resolves() -> None:
|
||||
"""The documented `toolchain: arduino` YAML key selects the native path."""
|
||||
from esphome.components.esp8266 import _resolve_toolchain
|
||||
from esphome.const import CONF_TOOLCHAIN
|
||||
|
||||
CORE.toolchain = None
|
||||
_resolve_toolchain({CONF_TOOLCHAIN: Toolchain.ARDUINO})
|
||||
assert CORE.toolchain == Toolchain.ARDUINO
|
||||
@@ -112,8 +112,6 @@ def test_yaml_toolchain_key_resolves() -> None:
|
||||
|
||||
def test_yaml_toolchain_key_defaults_to_platformio() -> None:
|
||||
CORE.toolchain = None
|
||||
from esphome.components.esp8266 import _resolve_toolchain
|
||||
|
||||
_resolve_toolchain({})
|
||||
assert CORE.toolchain == Toolchain.PLATFORMIO
|
||||
|
||||
@@ -122,8 +120,6 @@ def test_decode_pc_native_missing_tools_warns_once(
|
||||
tmp_path: Path, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""A stack dump of many addresses produces one missing-tool warning."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from esphome.components import esp8266
|
||||
|
||||
esp8266._warn_missing_decode_tool.cache_clear()
|
||||
|
||||
+35
-102
@@ -7150,54 +7150,24 @@ def test_upload_using_esptool_arduino_toolchain(
|
||||
assert cmd_list[firmware_offset_idx + 1] == str(CORE.firmware_bin)
|
||||
|
||||
|
||||
def test_write_cpp_file_arduino_toolchain_writes_no_project(tmp_path: Path) -> None:
|
||||
"""The native ESP8266 Arduino toolchain generates its project at compile
|
||||
time, so write_cpp_file must not write a platformio.ini."""
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
|
||||
with (
|
||||
patch("esphome.writer.write_cpp") as mock_write_cpp,
|
||||
patch("esphome.build_gen.platformio.write_project") as mock_pio_project,
|
||||
patch.object(
|
||||
type(CORE), "cpp_main_section", new_callable=PropertyMock
|
||||
) as mock_section,
|
||||
):
|
||||
mock_section.return_value = ""
|
||||
assert main.write_cpp_file() == 0
|
||||
|
||||
mock_write_cpp.assert_called_once()
|
||||
mock_pio_project.assert_not_called()
|
||||
|
||||
|
||||
def test_write_cpp_file_platformio_toolchain_writes_project(tmp_path: Path) -> None:
|
||||
"""The default toolchain writes the PlatformIO project files."""
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test")
|
||||
|
||||
with (
|
||||
patch("esphome.writer.write_cpp") as mock_write_cpp,
|
||||
patch("esphome.build_gen.platformio.write_project") as mock_pio_project,
|
||||
patch.object(
|
||||
type(CORE), "cpp_main_section", new_callable=PropertyMock
|
||||
) as mock_section,
|
||||
):
|
||||
mock_section.return_value = ""
|
||||
assert main.write_cpp_file() == 0
|
||||
|
||||
mock_write_cpp.assert_called_once()
|
||||
mock_pio_project.assert_called_once()
|
||||
|
||||
|
||||
def test_write_cpp_file_arduino_toolchain_other_platform_falls_through(
|
||||
tmp_path: Path,
|
||||
@pytest.mark.parametrize(
|
||||
("toolchain", "pio_project_written"),
|
||||
[
|
||||
# The native toolchain generates its project at compile time, so
|
||||
# write_cpp_file must not write a platformio.ini; the default
|
||||
# toolchain writes the PlatformIO project files.
|
||||
(Toolchain.ARDUINO, False),
|
||||
(None, True),
|
||||
],
|
||||
)
|
||||
def test_write_cpp_file_project_generation_follows_toolchain(
|
||||
tmp_path: Path, toolchain: Toolchain | None, pio_project_written: bool
|
||||
) -> None:
|
||||
"""The 'arduino' toolchain is ESP8266-only; other platforms keep the
|
||||
PlatformIO project generation."""
|
||||
setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test")
|
||||
CORE.toolchain = toolchain
|
||||
|
||||
with (
|
||||
patch("esphome.writer.write_cpp"),
|
||||
patch("esphome.writer.write_cpp") as mock_write_cpp,
|
||||
patch("esphome.build_gen.platformio.write_project") as mock_pio_project,
|
||||
patch.object(
|
||||
type(CORE), "cpp_main_section", new_callable=PropertyMock
|
||||
@@ -7206,7 +7176,8 @@ def test_write_cpp_file_arduino_toolchain_other_platform_falls_through(
|
||||
mock_section.return_value = ""
|
||||
assert main.write_cpp_file() == 0
|
||||
|
||||
mock_pio_project.assert_called_once()
|
||||
mock_write_cpp.assert_called_once()
|
||||
assert mock_pio_project.called is pio_project_written
|
||||
|
||||
|
||||
def test_command_idedata_arduino_prints_json(
|
||||
@@ -7238,33 +7209,34 @@ def test_command_idedata_arduino_no_build_errors(tmp_path: Path) -> None:
|
||||
assert result == 1
|
||||
|
||||
|
||||
def test_command_analyze_memory_arduino_toolchain(
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "toolchain", "module"),
|
||||
[
|
||||
(PLATFORM_ESP8266, Toolchain.ARDUINO, "esphome.arduino8266.toolchain"),
|
||||
(PLATFORM_ESP32, Toolchain.ESP_IDF, "esphome.espidf.toolchain"),
|
||||
],
|
||||
)
|
||||
def test_command_analyze_memory_native_toolchains(
|
||||
tmp_path: Path,
|
||||
mock_write_cpp: Mock,
|
||||
mock_compile_program: Mock,
|
||||
mock_get_esphome_components: Mock,
|
||||
mock_memory_analyzer_cli: Mock,
|
||||
mock_ram_strings_analyzer: Mock,
|
||||
platform: str,
|
||||
toolchain: Toolchain,
|
||||
module: str,
|
||||
) -> None:
|
||||
"""analyze-memory uses the native toolchain's binutils under
|
||||
'toolchain: arduino' instead of falling into the PlatformIO branch."""
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test_device")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
"""analyze-memory uses the native toolchain's binutils instead of
|
||||
falling into the PlatformIO branch."""
|
||||
setup_core(platform=platform, tmp_path=tmp_path, name="test_device")
|
||||
CORE.toolchain = toolchain
|
||||
|
||||
config = {CONF_ESPHOME: {CONF_NAME: "test_device"}}
|
||||
with (
|
||||
patch(
|
||||
"esphome.arduino8266.toolchain.get_objdump_path",
|
||||
return_value=Path("/tc/objdump"),
|
||||
),
|
||||
patch(
|
||||
"esphome.arduino8266.toolchain.get_readelf_path",
|
||||
return_value=Path("/tc/readelf"),
|
||||
),
|
||||
patch(
|
||||
"esphome.arduino8266.toolchain.get_elf_path",
|
||||
return_value=Path("/build/firmware.elf"),
|
||||
),
|
||||
patch(f"{module}.get_objdump_path", return_value=Path("/tc/objdump")),
|
||||
patch(f"{module}.get_readelf_path", return_value=Path("/tc/readelf")),
|
||||
patch(f"{module}.get_elf_path", return_value=Path("/build/firmware.elf")),
|
||||
):
|
||||
result = command_analyze_memory(MockArgs(), config)
|
||||
|
||||
@@ -7279,45 +7251,6 @@ def test_command_analyze_memory_arduino_toolchain(
|
||||
)
|
||||
|
||||
|
||||
def test_command_analyze_memory_esp_idf_toolchain(
|
||||
tmp_path: Path,
|
||||
mock_write_cpp: Mock,
|
||||
mock_compile_program: Mock,
|
||||
mock_get_esphome_components: Mock,
|
||||
mock_memory_analyzer_cli: Mock,
|
||||
mock_ram_strings_analyzer: Mock,
|
||||
) -> None:
|
||||
"""analyze-memory uses the ESP-IDF toolchain's binutils natively."""
|
||||
setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device")
|
||||
CORE.toolchain = Toolchain.ESP_IDF
|
||||
|
||||
config = {CONF_ESPHOME: {CONF_NAME: "test_device"}}
|
||||
with (
|
||||
patch(
|
||||
"esphome.espidf.toolchain.get_objdump_path",
|
||||
return_value=Path("/tc/objdump"),
|
||||
),
|
||||
patch(
|
||||
"esphome.espidf.toolchain.get_readelf_path",
|
||||
return_value=Path("/tc/readelf"),
|
||||
),
|
||||
patch(
|
||||
"esphome.espidf.toolchain.get_elf_path",
|
||||
return_value=Path("/build/firmware.elf"),
|
||||
),
|
||||
):
|
||||
result = command_analyze_memory(MockArgs(), config)
|
||||
|
||||
assert result == 0
|
||||
mock_memory_analyzer_cli.assert_called_once_with(
|
||||
str(Path("/build/firmware.elf")),
|
||||
str(Path("/tc/objdump")),
|
||||
str(Path("/tc/readelf")),
|
||||
set(),
|
||||
idedata=None,
|
||||
)
|
||||
|
||||
|
||||
def test_command_idedata_incompatible_toolchain(tmp_path: Path) -> None:
|
||||
"""A non-native, non-platformio toolchain errors out cleanly."""
|
||||
setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path)
|
||||
|
||||
Reference in New Issue
Block a user