diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 679aff06b5e..73455926b33 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -142,7 +142,7 @@ message HelloResponse { // A string identifying the server (ESP); like client info this may be empty // and only exists for debugging/logging purposes. // Currently set to ESPHOME_VERSION string literal. - string server_info = 3 [(max_data_length) = 32, (force) = true]; + string server_info = 3 [(max_data_length) = 32, (force) = true, (progmem) = true]; // The name of the server (App.get_name() - device hostname) // max_data_length matches ESPHOME_DEVICE_NAME_MAX_LEN (validated by validate_hostname) @@ -278,7 +278,7 @@ message DeviceInfoResponse { string mac_address = 3 [(max_data_length) = 17, (force) = true]; // A string describing the ESPHome version. For example "1.10.0" - string esphome_version = 4 [(max_data_length) = 32, (force) = true]; + string esphome_version = 4 [(max_data_length) = 32, (force) = true, (progmem) = true]; // A string describing the date of compilation, this is generated by the compiler // and therefore may not be in the same format all the time. @@ -287,14 +287,14 @@ message DeviceInfoResponse { // The model of the board. For example NodeMCU // max_data_length matches core/config.BOARD_MAX_LENGTH (validated in platform schemas) - string model = 6 [(max_data_length) = 127, (force) = true]; + string model = 6 [(max_data_length) = 127, (force) = true, (progmem) = true]; bool has_deep_sleep = 7 [(field_ifdef) = "USE_DEEP_SLEEP"]; // The esphome project details if set // max_data_length matches core/config.PROJECT_MAX_LENGTH - string project_name = 8 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME"]; - string project_version = 9 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME"]; + string project_name = 8 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME", (progmem) = true]; + string project_version = 9 [(max_data_length) = 127, (force) = true, (field_ifdef) = "ESPHOME_PROJECT_NAME", (progmem) = true]; uint32 webserver_port = 10 [(field_ifdef) = "USE_WEBSERVER"]; @@ -304,7 +304,7 @@ message DeviceInfoResponse { // Superseded by DeviceCapabilitiesResponse.bluetooth_proxy as of API 1.15. uint32 bluetooth_proxy_feature_flags = 15 [(field_ifdef) = "USE_BLUETOOTH_PROXY"]; - string manufacturer = 12 [(max_data_length) = 20, (force) = true]; + string manufacturer = 12 [(max_data_length) = 20, (force) = true, (progmem) = true]; string friendly_name = 13 [(max_data_length) = 120, (force) = true]; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bcb6486a84d..83269d07653 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -28,6 +28,7 @@ #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/progmem.h" #include "esphome/core/version.h" #ifdef USE_PROVISIONING #include "esphome/components/provisioning/provisioning.h" @@ -84,8 +85,6 @@ static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * // 28-30s. See https://github.com/esphome/esphome/issues/14999 static constexpr uint32_t HANDSHAKE_TIMEOUT_MS = 60000; -static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION); - // Cross-validate C++ constants against proto max_data_length annotations in api.proto static_assert(MAC_ADDRESS_PRETTY_BUFFER_SIZE - 1 == 17, "Update max_data_length for mac_address/bluetooth_mac_address in api.proto"); @@ -1899,6 +1898,11 @@ void APIConnection::complete_authentication_() { #endif } +// A flash string on ESP8266, a mergeable literal elsewhere; #undef after the device info reply +#define PROGMEM_STRING_REF(s) ProgmemStringRef(ESPHOME_PSTR(s), sizeof(s) - 1) +// One flash copy for the hello and device info replies +static ProgmemStringRef esphome_version_ref() { return PROGMEM_STRING_REF(ESPHOME_VERSION); } + bool APIConnection::send_hello_response_(const HelloRequest &msg) { // Copy client name with truncation if needed (set_client_name handles truncation) this->helper_->set_client_name(msg.client_info.c_str(), msg.client_info.size()); @@ -1914,7 +1918,7 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) { resp.api_version_major = 1; resp.api_version_minor = 19; // Send only the version string - the client only logs this for debugging and doesn't use it otherwise - resp.server_info = ESPHOME_VERSION_REF; + resp.server_info = esphome_version_ref(); resp.name = StringRef(App.get_name()); #ifdef USE_PROVISIONING @@ -1969,14 +1973,13 @@ bool APIConnection::send_device_info_response_() { format_mac_addr_upper(mac, mac_address); resp.mac_address = StringRef(mac_address); - resp.esphome_version = ESPHOME_VERSION_REF; + resp.esphome_version = esphome_version_ref(); // Stack buffer for build time string char build_time_str[Application::BUILD_TIME_STR_SIZE]; App.get_build_time_string(build_time_str); resp.compilation_time = StringRef(build_time_str); - // Manufacturer string - define once, handle ESP8266 PROGMEM separately #if defined(USE_ESP8266) || defined(USE_ESP32) #define ESPHOME_MANUFACTURER "Espressif" #elif defined(USE_RP2) @@ -1993,48 +1996,19 @@ bool APIConnection::send_device_info_response_() { #define ESPHOME_MANUFACTURER "Host" #endif -#ifdef USE_ESP8266 - // ESP8266 requires PROGMEM for flash storage, copy to stack for memcpy compatibility - static const char MANUFACTURER_PROGMEM[] PROGMEM = ESPHOME_MANUFACTURER; - char manufacturer_buf[sizeof(MANUFACTURER_PROGMEM)]; - memcpy_P(manufacturer_buf, MANUFACTURER_PROGMEM, sizeof(MANUFACTURER_PROGMEM)); - resp.manufacturer = StringRef(manufacturer_buf, sizeof(MANUFACTURER_PROGMEM) - 1); -#else - static constexpr auto MANUFACTURER = StringRef::from_lit(ESPHOME_MANUFACTURER); - resp.manufacturer = MANUFACTURER; -#endif + resp.manufacturer = PROGMEM_STRING_REF(ESPHOME_MANUFACTURER); static_assert(sizeof(ESPHOME_MANUFACTURER) - 1 <= 20, "Update max_data_length for manufacturer in api.proto"); #undef ESPHOME_MANUFACTURER -#ifdef USE_ESP8266 - static const char MODEL_PROGMEM[] PROGMEM = ESPHOME_BOARD; - char model_buf[sizeof(MODEL_PROGMEM)]; - memcpy_P(model_buf, MODEL_PROGMEM, sizeof(MODEL_PROGMEM)); - resp.model = StringRef(model_buf, sizeof(MODEL_PROGMEM) - 1); -#else - static constexpr auto MODEL = StringRef::from_lit(ESPHOME_BOARD); - resp.model = MODEL; -#endif + resp.model = PROGMEM_STRING_REF(ESPHOME_BOARD); #ifdef USE_DEEP_SLEEP resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; #endif #ifdef ESPHOME_PROJECT_NAME -#ifdef USE_ESP8266 - static const char PROJECT_NAME_PROGMEM[] PROGMEM = ESPHOME_PROJECT_NAME; - static const char PROJECT_VERSION_PROGMEM[] PROGMEM = ESPHOME_PROJECT_VERSION; - char project_name_buf[sizeof(PROJECT_NAME_PROGMEM)]; - char project_version_buf[sizeof(PROJECT_VERSION_PROGMEM)]; - memcpy_P(project_name_buf, PROJECT_NAME_PROGMEM, sizeof(PROJECT_NAME_PROGMEM)); - memcpy_P(project_version_buf, PROJECT_VERSION_PROGMEM, sizeof(PROJECT_VERSION_PROGMEM)); - resp.project_name = StringRef(project_name_buf, sizeof(PROJECT_NAME_PROGMEM) - 1); - resp.project_version = StringRef(project_version_buf, sizeof(PROJECT_VERSION_PROGMEM) - 1); -#else - static constexpr auto PROJECT_NAME = StringRef::from_lit(ESPHOME_PROJECT_NAME); - static constexpr auto PROJECT_VERSION = StringRef::from_lit(ESPHOME_PROJECT_VERSION); - resp.project_name = PROJECT_NAME; - resp.project_version = PROJECT_VERSION; -#endif + resp.project_name = PROGMEM_STRING_REF(ESPHOME_PROJECT_NAME); + resp.project_version = PROGMEM_STRING_REF(ESPHOME_PROJECT_VERSION); #endif +#undef PROGMEM_STRING_REF #ifdef USE_WEBSERVER resp.webserver_port = USE_WEBSERVER_PORT; #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 8f7b622bc96..57cf7e70f61 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -31,7 +31,7 @@ uint8_t *HelloResponse::encode_msg(const void *self, uint8_t *__restrict__ pos P const auto &msg = *static_cast(self); pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.api_version_major); pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 2, msg.api_version_minor); - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.server_info); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.server_info); pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 34, msg.name); return pos; } @@ -117,17 +117,17 @@ uint8_t *DeviceInfoResponse::encode_msg(const void *self, uint8_t *__restrict__ const auto &msg = *static_cast(self); pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 18, msg.name); pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.mac_address); - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 34, msg.esphome_version); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 34, msg.esphome_version); pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 42, msg.compilation_time); - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 50, msg.model); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 50, msg.model); #ifdef USE_DEEP_SLEEP pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.has_deep_sleep); #endif #ifdef ESPHOME_PROJECT_NAME - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 66, msg.project_name); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 66, msg.project_name); #endif #ifdef ESPHOME_PROJECT_NAME - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 74, msg.project_version); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 74, msg.project_version); #endif #ifdef USE_WEBSERVER pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.webserver_port); @@ -135,7 +135,7 @@ uint8_t *DeviceInfoResponse::encode_msg(const void *self, uint8_t *__restrict__ #ifdef USE_BLUETOOTH_PROXY pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 15, msg.bluetooth_proxy_feature_flags); #endif - pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 98, msg.manufacturer); + pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 98, msg.manufacturer); pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 106, msg.friendly_name); #ifdef USE_VOICE_ASSISTANT pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 17, msg.voice_assistant_feature_flags); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index fd12882dc9a..4c3dbf6df7c 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -466,7 +466,7 @@ class HelloResponse final : public ProtoMessage { #endif uint32_t api_version_major{0}; uint32_t api_version_minor{0}; - StringRef server_info{}; + ProgmemStringRef server_info{}; StringRef name{}; static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM); uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const { @@ -608,17 +608,17 @@ class DeviceInfoResponse final : public ProtoMessage { #endif StringRef name{}; StringRef mac_address{}; - StringRef esphome_version{}; + ProgmemStringRef esphome_version{}; StringRef compilation_time{}; - StringRef model{}; + ProgmemStringRef model{}; #ifdef USE_DEEP_SLEEP bool has_deep_sleep{false}; #endif #ifdef ESPHOME_PROJECT_NAME - StringRef project_name{}; + ProgmemStringRef project_name{}; #endif #ifdef ESPHOME_PROJECT_NAME - StringRef project_version{}; + ProgmemStringRef project_version{}; #endif #ifdef USE_WEBSERVER uint32_t webserver_port{0}; @@ -626,7 +626,7 @@ class DeviceInfoResponse final : public ProtoMessage { #ifdef USE_BLUETOOTH_PROXY uint32_t bluetooth_proxy_feature_flags{0}; #endif - StringRef manufacturer{}; + ProgmemStringRef manufacturer{}; StringRef friendly_name{}; #ifdef USE_VOICE_ASSISTANT uint32_t voice_assistant_feature_flags{0}; diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index 688ec48e6e6..757c569cc89 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -970,7 +970,7 @@ const char *HelloResponse::dump_to(DumpBuffer &out) const { MessageDumpHelper helper(out, ESPHOME_PSTR("HelloResponse")); dump_field(out, ESPHOME_PSTR("api_version_major"), this->api_version_major); dump_field(out, ESPHOME_PSTR("api_version_minor"), this->api_version_minor); - dump_field(out, ESPHOME_PSTR("server_info"), this->server_info); + dump_progmem_string_field(out, ESPHOME_PSTR("server_info"), this->server_info); dump_field(out, ESPHOME_PSTR("name"), this->name); return out.c_str(); } @@ -1021,17 +1021,17 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const { MessageDumpHelper helper(out, ESPHOME_PSTR("DeviceInfoResponse")); dump_field(out, ESPHOME_PSTR("name"), this->name); dump_field(out, ESPHOME_PSTR("mac_address"), this->mac_address); - dump_field(out, ESPHOME_PSTR("esphome_version"), this->esphome_version); + dump_progmem_string_field(out, ESPHOME_PSTR("esphome_version"), this->esphome_version); dump_field(out, ESPHOME_PSTR("compilation_time"), this->compilation_time); - dump_field(out, ESPHOME_PSTR("model"), this->model); + dump_progmem_string_field(out, ESPHOME_PSTR("model"), this->model); #ifdef USE_DEEP_SLEEP dump_field(out, ESPHOME_PSTR("has_deep_sleep"), this->has_deep_sleep); #endif #ifdef ESPHOME_PROJECT_NAME - dump_field(out, ESPHOME_PSTR("project_name"), this->project_name); + dump_progmem_string_field(out, ESPHOME_PSTR("project_name"), this->project_name); #endif #ifdef ESPHOME_PROJECT_NAME - dump_field(out, ESPHOME_PSTR("project_version"), this->project_version); + dump_progmem_string_field(out, ESPHOME_PSTR("project_version"), this->project_version); #endif #ifdef USE_WEBSERVER dump_field(out, ESPHOME_PSTR("webserver_port"), this->webserver_port); @@ -1039,7 +1039,7 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const { #ifdef USE_BLUETOOTH_PROXY dump_field(out, ESPHOME_PSTR("bluetooth_proxy_feature_flags"), this->bluetooth_proxy_feature_flags); #endif - dump_field(out, ESPHOME_PSTR("manufacturer"), this->manufacturer); + dump_progmem_string_field(out, ESPHOME_PSTR("manufacturer"), this->manufacturer); dump_field(out, ESPHOME_PSTR("friendly_name"), this->friendly_name); #ifdef USE_VOICE_ASSISTANT dump_field(out, ESPHOME_PSTR("voice_assistant_feature_flags"), this->voice_assistant_feature_flags); diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 2ddc97efb5f..d9f29259d03 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -559,7 +559,7 @@ void Application::get_comment_string(std::span b uint32_t Application::get_config_hash() { return ESPHOME_CONFIG_HASH; } -uint32_t Application::get_config_version_hash() { return fnv1a_hash_extend(ESPHOME_CONFIG_HASH, ESPHOME_VERSION); } +uint32_t Application::get_config_version_hash() { return ESPHOME_CONFIG_VERSION_HASH; } time_t Application::get_build_time() { return ESPHOME_BUILD_TIME; } diff --git a/esphome/core/build_info_data.h b/esphome/core/build_info_data.h index 02bb465e44f..c33edbdeeee 100644 --- a/esphome/core/build_info_data.h +++ b/esphome/core/build_info_data.h @@ -5,8 +5,9 @@ // // This file is only used by static analyzers and IDEs. -#define ESPHOME_CONFIG_HASH 0x12345678U // NOLINT -#define ESPHOME_BUILD_TIME 1700000000 // NOLINT -#define ESPHOME_COMMENT_SIZE 1 // NOLINT +#define ESPHOME_CONFIG_HASH 0x12345678U // NOLINT +#define ESPHOME_CONFIG_VERSION_HASH 0x9abcdef0U // NOLINT +#define ESPHOME_BUILD_TIME 1700000000 // NOLINT +#define ESPHOME_COMMENT_SIZE 1 // NOLINT static const char ESPHOME_BUILD_TIME_STR[] = "2024-01-01 00:00:00 +0000"; static const char ESPHOME_COMMENT_STR[] = ""; diff --git a/esphome/writer.py b/esphome/writer.py index e6c0c0e2e90..d5cf622c2c1 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -472,6 +472,7 @@ def generate_build_info_data_h() -> str: namespace esphome { extern const uint32_t ESPHOME_CONFIG_HASH; +extern const uint32_t ESPHOME_CONFIG_VERSION_HASH; extern const time_t ESPHOME_BUILD_TIME; extern const size_t ESPHOME_COMMENT_SIZE; #ifdef USE_ESP8266 @@ -500,9 +501,14 @@ def generate_build_info_data_cpp( comment_size = len(comment.encode("utf-8")) + 1 # +1 for NUL return f"""// Auto-generated build_info data #include "esphome/core/build_info_data.h" +#include "esphome/core/helpers.h" +#include "esphome/core/version.h" namespace esphome {{ const uint32_t ESPHOME_CONFIG_HASH = 0x{config_hash:08x}U; // NOLINT +// Folded at compile time, so the version string is not kept in RAM +static constexpr uint32_t CONFIG_VERSION_HASH = fnv1a_hash_extend(0x{config_hash:08x}U, ESPHOME_VERSION); +const uint32_t ESPHOME_CONFIG_VERSION_HASH = CONFIG_VERSION_HASH; // NOLINT const time_t ESPHOME_BUILD_TIME = {build_time}; // NOLINT const size_t ESPHOME_COMMENT_SIZE = {comment_size}; // NOLINT #ifdef USE_ESP8266 diff --git a/tests/benchmarks/components/api/bench_proto_encode.cpp b/tests/benchmarks/components/api/bench_proto_encode.cpp index e1383e89900..f8d836489f2 100644 --- a/tests/benchmarks/components/api/bench_proto_encode.cpp +++ b/tests/benchmarks/components/api/bench_proto_encode.cpp @@ -123,7 +123,7 @@ static void Encode_HelloResponse(benchmark::State &state) { HelloResponse msg; msg.api_version_major = 1; msg.api_version_minor = 10; - msg.server_info = StringRef::from_lit("esphome v2026.3.0"); + msg.server_info = ProgmemStringRef::from_lit("esphome v2026.3.0"); msg.name = StringRef::from_lit("living-room-sensor"); uint32_t size = msg.calculate_size(); (void) buffer.resize(size); @@ -204,10 +204,10 @@ static DeviceInfoResponse make_device_info_response() { DeviceInfoResponse msg; msg.name = StringRef::from_lit("living-room-sensor"); msg.mac_address = StringRef::from_lit("AA:BB:CC:DD:EE:FF"); - msg.esphome_version = StringRef::from_lit("2026.3.0"); + msg.esphome_version = ProgmemStringRef::from_lit("2026.3.0"); msg.compilation_time = StringRef::from_lit("Mar 16 2026, 12:00:00"); - msg.model = StringRef::from_lit("esp32-poe-iso"); - msg.manufacturer = StringRef::from_lit("Olimex"); + msg.model = ProgmemStringRef::from_lit("esp32-poe-iso"); + msg.manufacturer = ProgmemStringRef::from_lit("Olimex"); msg.friendly_name = StringRef::from_lit("Living Room Sensor"); #ifdef USE_DEVICES for (uint32_t i = 0; i < ESPHOME_DEVICE_COUNT && i < 20; i++) { diff --git a/tests/unit_tests/test_writer.py b/tests/unit_tests/test_writer.py index f03a42b6e8d..4575371822a 100644 --- a/tests/unit_tests/test_writer.py +++ b/tests/unit_tests/test_writer.py @@ -2190,6 +2190,7 @@ def test_generate_build_info_data_h_format() -> None: assert "#pragma once" in result assert "extern const uint32_t ESPHOME_CONFIG_HASH;" in result + assert "extern const uint32_t ESPHOME_CONFIG_VERSION_HASH;" in result assert "extern const time_t ESPHOME_BUILD_TIME;" in result assert "extern const size_t ESPHOME_COMMENT_SIZE;" in result assert "extern const char ESPHOME_BUILD_TIME_STR[]" in result @@ -2206,6 +2207,13 @@ def test_generate_build_info_data_h_esp8266_progmem() -> None: assert "PROGMEM" in result +def test_generate_build_info_data_cpp_config_version_hash() -> None: + """The config version hash is folded from the config hash and ESPHOME_VERSION.""" + result = generate_build_info_data_cpp(0x12345678, 1700000000, "", "") + assert "fnv1a_hash_extend(0x12345678U, ESPHOME_VERSION);" in result + assert "ESPHOME_CONFIG_VERSION_HASH = CONFIG_VERSION_HASH;" in result + + def test_generate_build_info_data_cpp_format() -> None: """Test generate_build_info_data_cpp produces correct data definitions.""" result = generate_build_info_data_cpp(