mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 12:33:10 +00:00
[esp32] Support esphome idedata with the native ESP-IDF toolchain (#17040)
This commit is contained in:
@@ -1771,6 +1771,21 @@ def command_update_all(args: ArgsProtocol) -> int | None:
|
|||||||
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
|
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
if CORE.using_toolchain_esp_idf:
|
||||||
|
# Native ESP-IDF derives idedata from the build's compile_commands.json,
|
||||||
|
# so the configuration must already be compiled.
|
||||||
|
from esphome.espidf import toolchain as espidf_toolchain
|
||||||
|
|
||||||
|
idedata = espidf_toolchain.get_idedata()
|
||||||
|
if idedata is None:
|
||||||
|
_LOGGER.error(
|
||||||
|
"No idedata available; compile the configuration first",
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(json.dumps(idedata, indent=2) + "\n")
|
||||||
|
return 0
|
||||||
|
|
||||||
if not CORE.using_toolchain_platformio:
|
if not CORE.using_toolchain_platformio:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"The idedata command is not compatible with %s toolchain",
|
"The idedata command is not compatible with %s toolchain",
|
||||||
|
|||||||
@@ -472,6 +472,7 @@ def get_idedata() -> dict | None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
data = idedata_from_build(compile_commands)
|
data = idedata_from_build(compile_commands)
|
||||||
|
data["prog_path"] = str(get_elf_path())
|
||||||
cache.parent.mkdir(parents=True, exist_ok=True)
|
cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
cache.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
cache.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
||||||
return data
|
return data
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ def test_get_idedata_generates_and_caches(setup_core: Path) -> None:
|
|||||||
result = toolchain.get_idedata()
|
result = toolchain.get_idedata()
|
||||||
|
|
||||||
mock_transform.assert_called_once()
|
mock_transform.assert_called_once()
|
||||||
assert result == {"cxx_path": "g++"}
|
prog_path = str(toolchain.get_elf_path())
|
||||||
assert json.loads(cache.read_text()) == {"cxx_path": "g++"}
|
assert result == {"cxx_path": "g++", "prog_path": prog_path}
|
||||||
|
assert json.loads(cache.read_text()) == {"cxx_path": "g++", "prog_path": prog_path}
|
||||||
|
|
||||||
|
|
||||||
def test_get_idedata_uses_cache_when_valid(setup_core: Path) -> None:
|
def test_get_idedata_uses_cache_when_valid(setup_core: Path) -> None:
|
||||||
@@ -127,7 +128,7 @@ def test_get_idedata_regenerates_when_compile_commands_newer(setup_core: Path) -
|
|||||||
result = toolchain.get_idedata()
|
result = toolchain.get_idedata()
|
||||||
|
|
||||||
mock_transform.assert_called_once()
|
mock_transform.assert_called_once()
|
||||||
assert result == {"cxx_path": "fresh"}
|
assert result == {"cxx_path": "fresh", "prog_path": str(toolchain.get_elf_path())}
|
||||||
|
|
||||||
|
|
||||||
def test_get_idedata_regenerates_on_corrupted_cache(setup_core: Path) -> None:
|
def test_get_idedata_regenerates_on_corrupted_cache(setup_core: Path) -> None:
|
||||||
@@ -147,7 +148,26 @@ def test_get_idedata_regenerates_on_corrupted_cache(setup_core: Path) -> None:
|
|||||||
result = toolchain.get_idedata()
|
result = toolchain.get_idedata()
|
||||||
|
|
||||||
mock_transform.assert_called_once()
|
mock_transform.assert_called_once()
|
||||||
assert result == {"cxx_path": "regen"}
|
assert result == {"cxx_path": "regen", "prog_path": str(toolchain.get_elf_path())}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_idedata_prog_path_points_at_firmware_elf(setup_core: Path) -> None:
|
||||||
|
"""The idedata exposes prog_path (the ELF) so consumers like build-action
|
||||||
|
can locate firmware.factory.bin / firmware.ota.bin as its siblings."""
|
||||||
|
compile_commands, _ = _setup_build(setup_core)
|
||||||
|
compile_commands.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
compile_commands.write_text("[]")
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"esphome.espidf.idedata.idedata_from_build",
|
||||||
|
return_value={"cxx_path": "g++"},
|
||||||
|
):
|
||||||
|
result = toolchain.get_idedata()
|
||||||
|
|
||||||
|
# Use Path semantics so the contract holds on Windows too (backslashes).
|
||||||
|
prog_path = Path(result["prog_path"])
|
||||||
|
assert prog_path.name == "firmware.elf"
|
||||||
|
assert prog_path.parent.name == "build"
|
||||||
|
|
||||||
|
|
||||||
def test_get_idf_env_sets_git_ceiling_directories(setup_core: Path) -> None:
|
def test_get_idf_env_sets_git_ceiling_directories(setup_core: Path) -> None:
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from esphome.__main__ import (
|
|||||||
command_clean_all,
|
command_clean_all,
|
||||||
command_config,
|
command_config,
|
||||||
command_config_hash,
|
command_config_hash,
|
||||||
|
command_idedata,
|
||||||
command_rename,
|
command_rename,
|
||||||
command_run,
|
command_run,
|
||||||
command_update_all,
|
command_update_all,
|
||||||
@@ -6257,3 +6258,28 @@ def test_command_run_defaults_subscribe_states_true(
|
|||||||
mock_run_logs.assert_called_once_with(
|
mock_run_logs.assert_called_once_with(
|
||||||
CORE.config, ["192.168.1.100"], subscribe_states=True
|
CORE.config, ["192.168.1.100"], subscribe_states=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_command_idedata_esp_idf_prints_json(capsys: CaptureFixture) -> None:
|
||||||
|
"""Under the native ESP-IDF toolchain, idedata is emitted as JSON."""
|
||||||
|
setup_core()
|
||||||
|
CORE.toolchain = Toolchain.ESP_IDF
|
||||||
|
data = {"cxx_path": "g++", "prog_path": "/build/firmware.elf"}
|
||||||
|
|
||||||
|
with patch("esphome.espidf.toolchain.get_idedata", return_value=data) as mock_get:
|
||||||
|
result = command_idedata(MagicMock(), CORE.config)
|
||||||
|
|
||||||
|
assert result == 0
|
||||||
|
mock_get.assert_called_once_with()
|
||||||
|
assert json.loads(capsys.readouterr().out) == data
|
||||||
|
|
||||||
|
|
||||||
|
def test_command_idedata_esp_idf_no_build_errors() -> None:
|
||||||
|
"""Under ESP-IDF, a missing build (no idedata) returns an error, not a crash."""
|
||||||
|
setup_core()
|
||||||
|
CORE.toolchain = Toolchain.ESP_IDF
|
||||||
|
|
||||||
|
with patch("esphome.espidf.toolchain.get_idedata", return_value=None):
|
||||||
|
result = command_idedata(MagicMock(), CORE.config)
|
||||||
|
|
||||||
|
assert result == 1
|
||||||
|
|||||||
Reference in New Issue
Block a user