Harden the shared toolchain check and pin every platform family's rejection

This commit is contained in:
J. Nick Koston
2026-08-20 14:41:23 -05:00
parent 8bf699cf79
commit 2861cbb749
5 changed files with 51 additions and 10 deletions
+1 -1
View File
@@ -314,8 +314,8 @@ BASE_SCHEMA = cv.Schema(
)
BASE_SCHEMA.add_extra(_detect_variant)
BASE_SCHEMA.add_extra(_update_core_data)
BASE_SCHEMA.add_extra(cv.require_platformio_toolchain("LibreTiny"))
BASE_SCHEMA.add_extra(_update_core_data)
def _configure_lwip(config: dict) -> None:
+11 -4
View File
@@ -107,6 +107,9 @@ from esphome.util import parse_esphome_version # noqa: F401
from esphome.voluptuous_schema import _Schema
from esphome.yaml_util import SensitiveStr, make_data_base
if typing.TYPE_CHECKING:
from esphome.types import ConfigType
_LOGGER = logging.getLogger(__name__)
# pylint: disable=invalid-name
@@ -2533,17 +2536,21 @@ def platformio_version_constraint(value):
return constraints
def check_supported_toolchain(platform_name: str, supported: tuple) -> None:
def check_supported_toolchain(
platform_name: str, supported: tuple[Toolchain, ...]
) -> None:
"""Raise when the resolved ``CORE.toolchain`` is not in ``supported``.
One message shape for every platform, so a ``--toolchain`` a platform
cannot serve always fails by name instead of silently building with a
different backend.
"""
if CORE.toolchain not in supported:
toolchain = CORE.toolchain
if toolchain is None or toolchain not in supported:
names = ", ".join(f"'{tc.value}'" for tc in supported)
raise Invalid(
f"Unsupported toolchain '{CORE.toolchain.value}' for "
f"Unsupported toolchain "
f"'{toolchain.value if toolchain else 'unresolved'}' for "
f"{platform_name}. Supported: {names}."
)
@@ -2555,7 +2562,7 @@ def require_platformio_toolchain(platform_name: str):
``--toolchain`` they cannot serve would silently build with PlatformIO.
"""
def validator(config):
def validator(config: ConfigType) -> ConfigType:
if CORE.toolchain is None:
CORE.toolchain = Toolchain.PLATFORMIO
check_supported_toolchain(platform_name, (Toolchain.PLATFORMIO,))
+8
View File
@@ -984,6 +984,12 @@ class EsphomeCore:
@property
def using_toolchain_arduino(self):
"""The native (PlatformIO-free) ESP8266 Arduino build backend.
Unlike ``using_arduino`` (the target *framework*, true for any
platform compiling Arduino code), this is a build *toolchain*
choice, like its ``using_toolchain_*`` siblings.
"""
return self.toolchain == Toolchain.ARDUINO
@property
@@ -1099,6 +1105,8 @@ class EsphomeCore:
return build_flag
def add_build_unflag(self, build_unflag: str) -> None:
# No warning for using_toolchain_arduino: the native ESP8266 build
# honors build_unflags (token-level, matching PlatformIO).
if self.using_toolchain_esp_idf:
# The native ESP-IDF build generator does not consume build_unflags
_LOGGER.warning(
+6 -5
View File
@@ -576,14 +576,15 @@ async def _add_platformio_options(pio_options: dict[str, str | list[str]]) -> No
for flag in vals:
cg.add_build_flag(flag)
elif key == "lib_deps":
# Routed through the regular library mechanism so the libraries
# are converted to IDF components like any other PIO library
# Routed through the regular library mechanism so the
# libraries reach the native backend's converter (IDF
# components, or the ESP8266 native library resolution)
for lib in vals:
_add_library_str(lib)
elif key == "lib_ignore":
# Read by the PIO-library-to-IDF-component conversion
# (generate_idf_components); filters both top-level libraries
# and dependencies discovered during conversion
# Read by the shared library conversion (lib_ignore_set in
# platformio/library.py) on both native backends; filters
# top-level libraries and discovered dependencies
cg.add_platformio_option(key, vals)
elif key != "upload_speed":
# upload_speed needs no handling: it is read from the raw
@@ -3181,3 +3181,28 @@ def test_require_platformio_toolchain() -> None:
CORE.toolchain = Toolchain.ARDUINO
with pytest.raises(Invalid, match="Unsupported toolchain 'arduino' for RP2"):
validator(config)
@pytest.mark.parametrize(
("platform", "minimal_config"),
[
("host", {}),
("rp2", {"board": "rpipicow"}),
("bk72xx", {"board": "generic-bk7231n-qfn32-tuya"}),
],
)
def test_every_platformio_only_platform_rejects_arduino_toolchain(
platform: str, minimal_config: dict
) -> None:
"""The invariant every native-toolchain gate relies on: a platform that
cannot serve a CLI toolchain rejects it at validation (esp32, esp8266,
and nrf52 pin this in their own suites)."""
import importlib
from esphome.const import Toolchain
from esphome.core import CORE
module = importlib.import_module(f"esphome.components.{platform}")
CORE.toolchain = Toolchain.ARDUINO
with pytest.raises(Invalid, match="Unsupported toolchain 'arduino'"):
module.CONFIG_SCHEMA(dict(minimal_config))