Fix ODR violation: generate api_pb2_defines.h for consistent ProtoVarInt layout

USE_API_VARINT64 was only defined in api_pb2.h, but proto.cpp (where
decode() and parse_wide() live) includes proto.h directly. This caused:

1. parse_wide() not compiled in proto.cpp (guarded by #ifdef)
2. decode() used 32-bit-only parse(), returning {} for varints > 5 bytes
3. ODR violation: ProtoVarInt was 4 bytes in proto.cpp but 8 bytes in
   api_pb2*.cpp translation units

For bluetooth_proxy, 48-bit BLE addresses encode as 7-byte varints.
The failed parse caused decode() to return early, leaving request_type
at its default value of 0 (BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT),
producing spurious "V1 connections removed" errors.

Fix: Generate api_pb2_defines.h with the USE_API_VARINT64 define and
include it from proto.h, ensuring all translation units see a consistent
ProtoVarInt layout.
This commit is contained in:
J. Nick Koston
2026-02-19 20:08:29 -06:00
parent 99a77e9549
commit 4e3b2abd5f
4 changed files with 25 additions and 14 deletions

View File

@@ -2,10 +2,6 @@
// See script/api_protobuf/api_protobuf.py
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_BLUETOOTH_PROXY
#define USE_API_VARINT64
#endif
#include "esphome/core/string_ref.h"
#include "proto.h"

View File

@@ -0,0 +1,8 @@
// This file was automatically generated with a tool.
// See script/api_protobuf/api_protobuf.py
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_BLUETOOTH_PROXY
#define USE_API_VARINT64
#endif

View File

@@ -1,5 +1,6 @@
#pragma once
#include "api_pb2_defines.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

View File

@@ -2593,22 +2593,28 @@ def main() -> None:
)
# Find the ifdef guard for 64-bit varint fields (int64/uint64/sint64).
# Emitted before proto.h so parse_wide_() and 64-bit accessors are available.
# Generated into api_pb2_defines.h so proto.h can include it, ensuring
# consistent ProtoVarInt layout across all translation units.
has_varint64, varint64_guard = get_varint64_ifdef(file, message_ifdef_map)
# Generate api_pb2_defines.h — included by proto.h to ensure all translation
# units see USE_API_VARINT64 consistently (avoids ODR violations in ProtoVarInt).
defines_content = FILE_HEADER
defines_content += "#pragma once\n\n"
defines_content += '#include "esphome/core/defines.h"\n'
if has_varint64:
defines_content += "\n".join(
wrap_with_ifdef(["#define USE_API_VARINT64"], varint64_guard)
)
defines_content += "\n"
with open(root / "api_pb2_defines.h", "w", encoding="utf-8") as f:
f.write(defines_content)
content = FILE_HEADER
content += """\
#pragma once
#include "esphome/core/defines.h"
"""
if has_varint64:
content += "\n".join(
wrap_with_ifdef(["#define USE_API_VARINT64"], varint64_guard)
)
content += "\n"
content += """\
#include "esphome/core/string_ref.h"
#include "proto.h"