From a58f3d34aa543724ec37dcb3646209c45eac4094 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 2 Oct 2026 07:59:36 -0500 Subject: [PATCH] [core] Compile util.cpp only on MQTT builds (#20003) --- esphome/core/config.py | 15 +++++++++++++-- esphome/core/util.cpp | 17 +++++------------ esphome/core/util.h | 8 ++++++++ tests/unit_tests/core/test_config.py | 17 +++++++++++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/esphome/core/config.py b/esphome/core/config.py index 281d430410..532ca7c36d 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -7,7 +7,10 @@ from pathlib import Path from esphome import automation, core import esphome.codegen as cg -from esphome.config_helpers import filter_source_files_from_platform +from esphome.config_helpers import ( + filter_source_files_from_defines, + filter_source_files_from_platform, +) import esphome.config_validation as cv from esphome.const import ( CONF_AREA, @@ -897,7 +900,7 @@ async def to_code(config: ConfigType) -> None: # Platform-specific source files for core -FILTER_SOURCE_FILES = filter_source_files_from_platform( +_filter_platform_sources = filter_source_files_from_platform( { "static_task.cpp": { PlatformFramework.ESP32_ARDUINO, @@ -950,3 +953,11 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( # as they are only included when needed by the preprocessor } ) + +# util.cpp holds only the out-of-line MQTT connectivity helpers; without +# USE_MQTT the header provides inline stubs and the file must not compile. +_filter_define_sources = filter_source_files_from_defines({"util.cpp": "USE_MQTT"}) + + +def FILTER_SOURCE_FILES() -> list[str]: + return _filter_platform_sources() + _filter_define_sources() diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index 54a7956163..4bca130e4a 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -1,23 +1,16 @@ #include "esphome/core/util.h" -#include "esphome/core/application.h" -#include "esphome/core/version.h" -#include "esphome/core/log.h" +// Compiled only on MQTT builds (core FILTER_SOURCE_FILES); without USE_MQTT +// the header provides inline stubs and this file must stay empty, so the +// guard protects builds that compile every source, such as clang-tidy. #ifdef USE_MQTT #include "esphome/components/mqtt/mqtt_client.h" -#endif namespace esphome { -bool mqtt_is_connected() { -#ifdef USE_MQTT - if (mqtt::global_mqtt_client != nullptr) { - return mqtt::global_mqtt_client->is_connected(); - } -#endif - return false; -} +bool mqtt_is_connected() { return mqtt::global_mqtt_client != nullptr && mqtt::global_mqtt_client->is_connected(); } bool remote_is_connected() { return api_is_connected() || mqtt_is_connected(); } } // namespace esphome +#endif diff --git a/esphome/core/util.h b/esphome/core/util.h index 8f90aa3411..aa3098ba65 100644 --- a/esphome/core/util.h +++ b/esphome/core/util.h @@ -25,9 +25,17 @@ ESPHOME_ALWAYS_INLINE inline bool api_is_connected() { return false; } #endif /// Return whether the node has an active connection to an MQTT broker +#ifdef USE_MQTT bool mqtt_is_connected(); +#else +inline bool mqtt_is_connected() { return false; } +#endif /// Return whether the node has any form of "remote" connection via the API or to an MQTT broker +#ifdef USE_MQTT bool remote_is_connected(); +#else +inline bool remote_is_connected() { return api_is_connected(); } +#endif } // namespace esphome diff --git a/tests/unit_tests/core/test_config.py b/tests/unit_tests/core/test_config.py index cfb9651e6b..7fbc2d1b39 100644 --- a/tests/unit_tests/core/test_config.py +++ b/tests/unit_tests/core/test_config.py @@ -1493,3 +1493,20 @@ async def test_add_platformio_options_native_arduino( assert "board_build.ldscript is ignored" in caplog.text assert "'arduino' toolchain" in caplog.text assert "upload_speed" not in caplog.text + + +def test_filter_source_files_drops_util_cpp_without_mqtt() -> None: + """util.cpp compiles only on MQTT builds; the header stubs it otherwise.""" + CORE.data[KEY_CORE] = { + KEY_TARGET_PLATFORM: "esp8266", + KEY_TARGET_FRAMEWORK: "arduino", + } + CORE.defines = set() + + excluded = config.FILTER_SOURCE_FILES() + assert "util.cpp" in excluded + # The platform map still contributes through the composed function. + assert "static_task.cpp" in excluded + + CORE.defines = {core.Define("USE_API"), core.Define("USE_MQTT")} + assert "util.cpp" not in config.FILTER_SOURCE_FILES()