diff --git a/esphome/espidf/framework.py b/esphome/espidf/framework.py index 18aa966bff1..b8e0d4cfca7 100644 --- a/esphome/espidf/framework.py +++ b/esphome/espidf/framework.py @@ -1,5 +1,6 @@ """ESP-IDF framework tools for ESPHome.""" +from ctypes.util import find_library import json import logging import os @@ -668,6 +669,14 @@ def _check_esphome_idf_framework_install( env=env, stream_output=True, ): + if platform.system() == "Linux" and find_library("usb-1.0") is None: + _LOGGER.error( + "libusb-1.0.so.0 was not found on this system and the ESP-IDF " + "tools need it (openocd fails its install check without it). " + "Install the libusb 1.0 package, e.g. libusb-1.0-0 " + "(Debian/Ubuntu), libusb1 (Fedora) or libusb (Alpine/Arch), " + "then run the build again." + ) raise RuntimeError(f"ESP-IDF {version} framework installation failure") _write_stamp(env_stamp_file, stamp_info) diff --git a/tests/unit_tests/test_espidf_framework.py b/tests/unit_tests/test_espidf_framework.py index de02a6b227e..a1af5ae54c4 100644 --- a/tests/unit_tests/test_espidf_framework.py +++ b/tests/unit_tests/test_espidf_framework.py @@ -478,6 +478,34 @@ def test_check_esp_idf_install_python_stamp_mismatch_rebuilds_venv( espidf_mocks.venv.assert_called_once() +@pytest.mark.parametrize( + ("lib", "expect_hint"), + [ + (None, True), + ("libusb-1.0.so.0", False), + ], +) +def test_check_esp_idf_install_failure_libusb_hint( + espidf_mocks: SimpleNamespace, + caplog: pytest.LogCaptureFixture, + lib: str | None, + expect_hint: bool, +) -> None: + """A failed tools install only shows the libusb hint when libusb-1.0 is + actually missing.""" + espidf_mocks.run_ok.return_value = False + # Fake Linux so the gate is exercised on all CI hosts; faking Linux is safe + # everywhere (unlike faking Windows, which pulls in winreg on other hosts) + with ( + patch("esphome.espidf.framework.find_library", return_value=lib), + patch("esphome.espidf.framework.platform.system", return_value="Linux"), + caplog.at_level(logging.ERROR, logger="esphome.espidf.framework"), + pytest.raises(RuntimeError, match="framework installation failure"), + ): + check_esp_idf_install(_IDF_VERSION, force=True) + assert ("libusb-1.0.so.0 was not found" in caplog.text) == expect_hint + + def test_check_esp_idf_install_unparseable_version( espidf_mocks: SimpleNamespace, ) -> None: