mirror of
https://github.com/esphome/esphome.git
synced 2026-05-23 11:16:52 +08:00
[api] Make api a buildable cpp unit test target
Three fixes so 'script/cpp_unit_test.py api' actually compiles instead
of crashing in build setup:
1. script/build_helpers.py: when adding transitive component
dependencies to the post-validation config, use {} (dict) instead
of [] (list) for non-MULTI_CONF components. socket's
FILTER_SOURCE_FILES (and any other code that subscripts
CORE.config[component] with a string key) was crashing because
socket got config = [] from setdefault.
2. esphome/components/api/api_pb2_service.cpp + the codegen in
script/api_protobuf/api_protobuf.py: wrap the generated
APIConnection::read_message_ definition in #ifdef USE_API. The
class itself is only declared inside #ifdef USE_API in
api_connection.h, so without the guard the .cpp fails to compile
in any build that pulls in the api source files without setting
USE_API (e.g. cpp unit tests of api dependencies).
This commit is contained in:
@@ -21,6 +21,7 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_API
|
||||
void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
|
||||
// Check authentication/connection requirements
|
||||
switch (msg_type) {
|
||||
@@ -706,5 +707,6 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // USE_API
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -177,14 +177,7 @@ async def to_code(config):
|
||||
|
||||
def FILTER_SOURCE_FILES() -> list[str]:
|
||||
"""Return list of socket implementation files that aren't selected by the user."""
|
||||
socket_config = CORE.config.get("socket")
|
||||
if not isinstance(socket_config, dict):
|
||||
# Config has not been validated yet (or socket isn't configured as a
|
||||
# mapping for this build, e.g. a C++ unit test build that skips config
|
||||
# validation). Don't filter -- all impl files are guarded by USE_*
|
||||
# defines so only the selected implementation contributes code anyway.
|
||||
return []
|
||||
impl = socket_config[CONF_IMPLEMENTATION]
|
||||
impl = CORE.config["socket"][CONF_IMPLEMENTATION]
|
||||
|
||||
# Build list of files to exclude based on selected implementation
|
||||
excluded = []
|
||||
|
||||
@@ -3577,8 +3577,13 @@ static const char *const TAG = "api.service";
|
||||
# Generate read_message_ as APIConnection method (not base class) so the compiler
|
||||
# can devirtualize and inline the on_* handler calls within the same class.
|
||||
# APIConnection declares this method in api_connection.h.
|
||||
# Guard with #ifdef USE_API since APIConnection itself is only defined when
|
||||
# USE_API is set; without this, builds that compile this .cpp without
|
||||
# USE_API (e.g. C++ unit tests for api dependencies) fail to find the
|
||||
# class declaration.
|
||||
|
||||
out = "void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {\n"
|
||||
out = "#ifdef USE_API\n"
|
||||
out += "void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {\n"
|
||||
|
||||
# Auth check block before dispatch switch
|
||||
out += " // Check authentication/connection requirements\n"
|
||||
@@ -3623,6 +3628,7 @@ static const char *const TAG = "api.service";
|
||||
out += " break;\n"
|
||||
out += " }\n"
|
||||
out += "}\n"
|
||||
out += "#endif // USE_API\n"
|
||||
cpp += out
|
||||
hpp += "};\n"
|
||||
|
||||
|
||||
@@ -324,8 +324,13 @@ def compile_and_get_binary(
|
||||
domain_list.append({CONF_PLATFORM: component})
|
||||
# Skip "core" — it's a pseudo-component handled by the build
|
||||
# system, not a real loadable component (get_component returns None)
|
||||
elif get_component(component_name) is not None:
|
||||
config.setdefault(component_name, [])
|
||||
elif (component := get_component(component_name)) is not None:
|
||||
# MULTI_CONF components store their config as a list of dicts,
|
||||
# everything else stores a single dict. Using the wrong shape
|
||||
# breaks code paths that subscript CORE.config[component] with
|
||||
# a string key (e.g. socket.FILTER_SOURCE_FILES).
|
||||
default = [] if component.multi_conf else {}
|
||||
config.setdefault(component_name, default)
|
||||
|
||||
# Register platforms from the extra config (benchmark.yaml) so
|
||||
# USE_SENSOR, USE_LIGHT, etc. defines are emitted without needing
|
||||
|
||||
Reference in New Issue
Block a user