[socket] Compile the lookup and client link helpers only when required (#20002)

This commit is contained in:
J. Nick Koston
2026-10-02 07:31:41 -05:00
committed by GitHub
parent 12b016e3d2
commit 3fd8f00e97
6 changed files with 62 additions and 1 deletions
+15 -1
View File
@@ -140,6 +140,17 @@ def require_wake_loop_threadsafe() -> None:
cg.add_define("USE_SOCKET_SELECT_SUPPORT")
def require_ipv4_resolve() -> None:
"""Compile the shared IPv4 lookup; call from a consumer's to_code."""
cg.add_define("USE_SOCKET_IPV4_RESOLVE")
def require_tcp_client_link() -> None:
"""Compile the reconnecting TCP client link; call from a consumer's to_code."""
require_ipv4_resolve()
cg.add_define("USE_SOCKET_TCP_CLIENT_LINK")
CONFIG_SCHEMA = cv.Schema(
{
cv.SplitDefault(
@@ -184,11 +195,14 @@ async def to_code(config: ConfigType) -> None:
# Each implementation file is fully #ifdef'd on the define set in to_code
# for the selected implementation.
# for the selected implementation. The helper files compile only for
# consumers that called the matching require_ function.
FILTER_SOURCE_FILES = filter_source_files_from_defines(
{
"lwip_raw_tcp_impl.cpp": "USE_SOCKET_IMPL_LWIP_TCP",
"bsd_sockets_impl.cpp": "USE_SOCKET_IMPL_BSD_SOCKETS",
"lwip_sockets_impl.cpp": "USE_SOCKET_IMPL_LWIP_SOCKETS",
"ipv4_resolve.cpp": "USE_SOCKET_IPV4_RESOLVE",
"tcp_client_link.cpp": "USE_SOCKET_TCP_CLIENT_LINK",
}
)
+2
View File
@@ -417,6 +417,8 @@
#define USE_SENDSPIN_VISUALIZER
#define USE_SENDSPIN_PORT 8928 // NOLINT
#define USE_SOCKET_IMPL_BSD_SOCKETS
#define USE_SOCKET_IPV4_RESOLVE
#define USE_SOCKET_TCP_CLIENT_LINK
#define USE_LWIP_FAST_SELECT
#define USE_SPEAKER
+3
View File
@@ -2,6 +2,7 @@
# shadowing the stdlib module -- no test_*.py (Python-level) tests can live here.
# required for cpp unit testing.
import esphome.codegen as cg
from esphome.components import socket as socket_component
from tests.testing_helpers import ComponentManifestOverride
@@ -12,5 +13,7 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
async def to_code_testing(config):
await real_to_code(config)
cg.add_define("USE_NETWORK_IPV6", True)
# The gtests link against the filtered helper files.
socket_component.require_tcp_client_link()
manifest.to_code = to_code_testing
@@ -1,4 +1,5 @@
import esphome.codegen as cg
from esphome.components import socket
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
@@ -18,5 +19,6 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config: ConfigType) -> None:
socket.require_ipv4_resolve()
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
@@ -1,4 +1,5 @@
import esphome.codegen as cg
from esphome.components import socket
from esphome.components.const import CONF_HOST
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_PORT
@@ -28,6 +29,7 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config: ConfigType) -> None:
socket.require_tcp_client_link()
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_host(config[CONF_HOST]))
@@ -0,0 +1,38 @@
"""Tests for the socket component's source filtering and require functions."""
from unittest.mock import patch
from esphome.components import socket
from esphome.core import Define
def test_helper_files_filtered_until_required() -> None:
"""ipv4_resolve.cpp and tcp_client_link.cpp compile only when required."""
with patch("esphome.config_helpers.CORE") as mock_core:
mock_core.defines = set()
filtered = socket.FILTER_SOURCE_FILES()
assert "ipv4_resolve.cpp" in filtered
assert "tcp_client_link.cpp" in filtered
mock_core.defines = {Define("USE_SOCKET_IPV4_RESOLVE")}
filtered = socket.FILTER_SOURCE_FILES()
assert "ipv4_resolve.cpp" not in filtered
assert "tcp_client_link.cpp" in filtered
mock_core.defines = {
Define("USE_SOCKET_IPV4_RESOLVE"),
Define("USE_SOCKET_TCP_CLIENT_LINK"),
}
filtered = socket.FILTER_SOURCE_FILES()
assert "ipv4_resolve.cpp" not in filtered
assert "tcp_client_link.cpp" not in filtered
def test_require_tcp_client_link_pulls_in_the_resolver() -> None:
"""require_tcp_client_link() sets both defines; the link reads the resolver."""
with patch.object(socket.cg, "add_define") as add_define:
socket.require_tcp_client_link()
assert {call.args[0] for call in add_define.call_args_list} == {
"USE_SOCKET_IPV4_RESOLVE",
"USE_SOCKET_TCP_CLIENT_LINK",
}