From 707658b385d6cdec2fe1579e89d7873e4a779676 Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Mon, 30 Mar 2026 12:25:10 -0400 Subject: [PATCH 01/11] Add grpc serialization --- source/serials/grpc_serial/CMakeLists.txt | 187 +++++++ .../serials/grpc_serial/include/grpc_serial.h | 60 +++ .../grpc_serial/include/grpc_serial_impl.h | 108 ++++ .../serials/grpc_serial/source/grpc_serial.c | 56 +++ .../grpc_serial/source/grpc_serial_impl.cpp | 466 ++++++++++++++++++ 5 files changed, 877 insertions(+) create mode 100644 source/serials/grpc_serial/CMakeLists.txt create mode 100644 source/serials/grpc_serial/include/grpc_serial.h create mode 100644 source/serials/grpc_serial/include/grpc_serial_impl.h create mode 100644 source/serials/grpc_serial/source/grpc_serial.c create mode 100644 source/serials/grpc_serial/source/grpc_serial_impl.cpp diff --git a/source/serials/grpc_serial/CMakeLists.txt b/source/serials/grpc_serial/CMakeLists.txt new file mode 100644 index 0000000000..2e8fddacc1 --- /dev/null +++ b/source/serials/grpc_serial/CMakeLists.txt @@ -0,0 +1,187 @@ +# Check if this serial is enabled +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) + return() +endif() + +# +# External dependencies +# + +find_package(Protobuf REQUIRED) +find_package(gRPC QUIET) + +if(NOT gRPC_FOUND) + include(InstallGRPC) + if(NOT gRPC_FOUND) + message(SEND_ERROR "gRPC libraries not found") + return() + endif() + set(GRPC_INSTALL TRUE) +endif() + +# +# Library name and options +# + +# Target name +set(target grpc_serial) + +# Exit here if required dependencies are not met +message(STATUS "Serial ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/grpc_serial.h + ${include_path}/grpc_serial_impl.h +) + +set(sources + ${source_path}/grpc_serial.c + ${source_path}/grpc_serial_impl.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create library +# + +# Build library +add_library(${target} MODULE + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + BUNDLE $,$> +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + $ # MetaCall includes + ${Protobuf_INCLUDE_DIRS} # Protobuf includes + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + # On macOS, we use dynamic lookup to avoid loading libmetacall twice + $<$>:${META_PROJECT_NAME}::metacall> + ${Protobuf_LIBRARIES} # libprotobuf + gRPC::grpc++ # libgrpc++ (brings in gRPC core) + PUBLIC + ${DEFAULT_LIBRARIES} + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + # On macOS, we use dynamic lookup for using existing symbols + $<$,$>:-Wl,-undefined,dynamic_lookup> + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + INTERFACE +) + +# +# Deployment +# + +# Library +install(TARGETS ${target} + EXPORT "${target}-export" COMPONENT dev + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime + LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime + ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev +) diff --git a/source/serials/grpc_serial/include/grpc_serial.h b/source/serials/grpc_serial/include/grpc_serial.h new file mode 100644 index 0000000000..e25f5cabc3 --- /dev/null +++ b/source/serials/grpc_serial/include/grpc_serial.h @@ -0,0 +1,60 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SERIAL_H +#define GRPC_SERIAL_H 1 + +/* -- Headers -- */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Methods -- */ + +/** +* @brief +* Instance of interface implementation +* +* @return +* Returns pointer to interface to be used by implementation +* +*/ +GRPC_SERIAL_API serial_interface grpc_serial_impl_interface_singleton(void); + +/** +* @brief +* Provide the module information +* +* @return +* Static string containing module information +* +*/ +GRPC_SERIAL_API const char *grpc_serial_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SERIAL_H */ diff --git a/source/serials/grpc_serial/include/grpc_serial_impl.h b/source/serials/grpc_serial/include/grpc_serial_impl.h new file mode 100644 index 0000000000..13aaeac832 --- /dev/null +++ b/source/serials/grpc_serial/include/grpc_serial_impl.h @@ -0,0 +1,108 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SERIAL_IMPL_H +#define GRPC_SERIAL_IMPL_H 1 + +/* -- Headers -- */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Methods -- */ + +/** +* @brief +* Retrieve extension supported by gRPC implementation +* +* @return +* Returns constant string representing serial extension +* +*/ +GRPC_SERIAL_API const char *grpc_serial_impl_extension(void); + +/** +* @brief +* Initialize gRPC (Protobuf) serial document implementation +* +* @return +* Returns pointer to serial document implementation on success, null pointer otherwise +* +*/ +GRPC_SERIAL_API serial_handle grpc_serial_impl_initialize(memory_allocator allocator); + +/** +* @brief +* Serialize with gRPC (Protobuf) document implementation @handle +* +* @param[in] handle +* Pointer to the serial document implementation +* +* @param[in] v +* Reference to the value is going to be serialized +* +* @param[out] size +* Size in bytes of the return buffer +* +* @return +* String with the value serialized on correct serialization, null otherwise +* +*/ +GRPC_SERIAL_API char *grpc_serial_impl_serialize(serial_handle handle, value v, size_t *size); + +/** +* @brief +* Deserialize with gRPC (Protobuf) document implementation @handle +* +* @param[in] handle +* Pointer to the serial document implementation +* +* @param[in] buffer +* Reference to the string is going to be deserialized +* +* @param[in] size +* Size in bytes of the string @buffer +* +* @return +* Pointer to value deserialized on correct serialization, null otherwise +* +*/ +GRPC_SERIAL_API value grpc_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); + +/** +* @brief +* Destroy gRPC (Protobuf) document implementation +* +* @return +* Returns zero on correct destruction, distinct from zero otherwise +* +*/ +GRPC_SERIAL_API int grpc_serial_impl_destroy(serial_handle handle); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SERIAL_IMPL_H */ diff --git a/source/serials/grpc_serial/source/grpc_serial.c b/source/serials/grpc_serial/source/grpc_serial.c new file mode 100644 index 0000000000..8bc8c26c53 --- /dev/null +++ b/source/serials/grpc_serial/source/grpc_serial.c @@ -0,0 +1,56 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include +#include + +/* -- Methods -- */ + +serial_interface grpc_serial_impl_interface_singleton(void) +{ + static struct serial_interface_type interface_instance_grpc = { + &grpc_serial_impl_extension, + &grpc_serial_impl_initialize, + &grpc_serial_impl_serialize, + &grpc_serial_impl_deserialize, + &grpc_serial_impl_destroy + }; + + return &interface_instance_grpc; +} + +const char *grpc_serial_print_info(void) +{ + static const char grpc_serial_info[] = + "gRPC Serial Plugin " METACALL_VERSION "\n" + "Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia \n" +#ifdef GRPC_SERIAL_STATIC_DEFINE + "Compiled as static library type\n" +#else + "Compiled as shared library type\n" +#endif + "\n"; + + return grpc_serial_info; +} diff --git a/source/serials/grpc_serial/source/grpc_serial_impl.cpp b/source/serials/grpc_serial/source/grpc_serial_impl.cpp new file mode 100644 index 0000000000..d7cfabb88b --- /dev/null +++ b/source/serials/grpc_serial/source/grpc_serial_impl.cpp @@ -0,0 +1,466 @@ + + +#include + +#include + +#include + +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wstrict-overflow" + #pragma clang diagnostic ignored "-Wshadow" + #pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-overflow" + #pragma GCC diagnostic ignored "-Wshadow" + #pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include +#include + +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif + +#include +#include +#include + + + +typedef struct grpc_document_type +{ + google::protobuf::Value impl; + memory_allocator allocator; + +} * grpc_document; + +/* -- Private Methods -- */ + +static void grpc_serial_impl_serialize_value(value v, google::protobuf::Value *pb_v); + +static char *grpc_serial_impl_document_stringify(grpc_document document, size_t *size); + +static value grpc_serial_impl_deserialize_value(const google::protobuf::Value *pb_v); + +/* -- Methods -- */ + +const char *grpc_serial_impl_extension() +{ + static const char extension[] = "grpc"; + + return extension; +} + +serial_handle grpc_serial_impl_initialize(memory_allocator allocator) +{ + grpc_document document = new grpc_document_type(); + + if (document == nullptr) + { + return NULL; + } + + document->allocator = allocator; + + return (serial_handle)document; +} + +/* ------------------------------------------------------------------------- + * Serialize: MetaCall value → google::protobuf::Value + * ---------------------------------------------------------------------- */ + +void grpc_serial_impl_serialize_value(value v, google::protobuf::Value *pb_v) +{ + type_id id = value_type_id(v); + + if (id == TYPE_BOOL) + { + pb_v->set_bool_value(value_to_bool(v) == 1L ? true : false); + } + else if (id == TYPE_CHAR) + { + char buf[2] = { value_to_char(v), '\0' }; + pb_v->set_string_value(buf); + } + else if (id == TYPE_SHORT) + { + pb_v->set_number_value(static_cast(value_to_short(v))); + } + else if (id == TYPE_INT) + { + pb_v->set_number_value(static_cast(value_to_int(v))); + } + else if (id == TYPE_LONG) + { + pb_v->set_number_value(static_cast(value_to_long(v))); + } + else if (id == TYPE_FLOAT) + { + pb_v->set_number_value(static_cast(value_to_float(v))); + } + else if (id == TYPE_DOUBLE) + { + pb_v->set_number_value(value_to_double(v)); + } + else if (id == TYPE_STRING) + { + const char *str = value_to_string(v); + size_t size = value_type_size(v); + /* size includes the NUL terminator */ + pb_v->set_string_value(str, size > 0 ? size - 1 : 0); + } + else if (id == TYPE_BUFFER) + { + /* + * Encode as: { "data": [byte0, byte1, ...], "length": N } + * Mirrors the RapidJSON layout so consumers can detect buffers. + */ + google::protobuf::Struct *st = pb_v->mutable_struct_value(); + + void *buffer = value_to_buffer(v); + size_t size = value_type_size(v); + + google::protobuf::Value data_val; + google::protobuf::ListValue *list = data_val.mutable_list_value(); + + for (size_t i = 0; i < size; ++i) + { + const unsigned char byte = *(static_cast(buffer) + i); + google::protobuf::Value *elem = list->add_values(); + elem->set_number_value(static_cast(byte)); + } + + (*st->mutable_fields())["data"] = data_val; + + google::protobuf::Value len_val; + len_val.set_number_value(static_cast(size)); + (*st->mutable_fields())["length"] = len_val; + } + else if (id == TYPE_ARRAY) + { + google::protobuf::ListValue *list = pb_v->mutable_list_value(); + + value *value_array = value_to_array(v); + size_t array_size = value_type_count(v); + + for (size_t i = 0; i < array_size; ++i) + { + grpc_serial_impl_serialize_value(value_array[i], list->add_values()); + } + } + else if (id == TYPE_MAP) + { + google::protobuf::Struct *st = pb_v->mutable_struct_value(); + + value *value_map = value_to_map(v); + size_t map_size = value_type_count(v); + + for (size_t i = 0; i < map_size; ++i) + { + value tupla = value_map[i]; + value *tupla_array = value_to_array(tupla); + + /* Key must be a string in Protobuf Struct */ + google::protobuf::Value key_pb; + grpc_serial_impl_serialize_value(tupla_array[0], &key_pb); + + std::string key_str; + if (key_pb.kind_case() == google::protobuf::Value::kStringValue) + { + key_str = key_pb.string_value(); + } + else + { + /* Non-string keys: convert number/bool to string representation */ + std::ostringstream oss; + if (key_pb.kind_case() == google::protobuf::Value::kNumberValue) + { + oss << key_pb.number_value(); + } + else if (key_pb.kind_case() == google::protobuf::Value::kBoolValue) + { + oss << (key_pb.bool_value() ? "true" : "false"); + } + else + { + oss << "(null)"; + } + key_str = oss.str(); + } + + grpc_serial_impl_serialize_value(tupla_array[1], &(*st->mutable_fields())[key_str]); + } + } + else if (id == TYPE_FUTURE) + { + pb_v->set_string_value("[Future]"); + } + else if (id == TYPE_FUNCTION) + { + pb_v->set_string_value("[Function]"); + } + else if (id == TYPE_CLASS) + { + pb_v->set_string_value("[Class]"); + } + else if (id == TYPE_OBJECT) + { + pb_v->set_string_value("[Object]"); + } + else if (id == TYPE_EXCEPTION) + { + exception ex = value_to_exception(v); + + google::protobuf::Struct *st = pb_v->mutable_struct_value(); + + auto &fields = *st->mutable_fields(); + + fields["message"].set_string_value(exception_message(ex)); + fields["label"].set_string_value(exception_label(ex)); + fields["code"].set_number_value(static_cast(exception_error_code(ex))); + fields["stacktrace"].set_string_value(exception_stacktrace(ex)); + } + else if (id == TYPE_THROWABLE) + { + throwable th = value_to_throwable(v); + + google::protobuf::Struct *st = pb_v->mutable_struct_value(); + + grpc_serial_impl_serialize_value(throwable_value(th), &(*st->mutable_fields())["ExceptionThrown"]); + } + else if (id == TYPE_PTR) + { + std::ostringstream oss; + oss << value_to_ptr(v); + pb_v->set_string_value(oss.str()); + } + else if (id == TYPE_NULL) + { + pb_v->set_null_value(google::protobuf::NULL_VALUE); + } +} + + +char *grpc_serial_impl_document_stringify(grpc_document document, size_t *size) +{ + std::string wire; + + if (!document->impl.SerializeToString(&wire)) + { + log_write("metacall", LOG_LEVEL_ERROR, "Failed to serialise Protobuf Value to binary wire format in gRPC implementation"); + return NULL; + } + + size_t wire_size = wire.size(); + size_t alloc_size = wire_size + 1; /* +1: NUL sentinel so callers can detect empty */ + + char *buffer_str = static_cast(memory_allocator_allocate(document->allocator, sizeof(char) * alloc_size)); + + if (buffer_str == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid string allocation for document stringification in gRPC implementation"); + return NULL; + } + + memcpy(buffer_str, wire.data(), wire_size); + buffer_str[wire_size] = '\0'; + + *size = alloc_size; + + return buffer_str; +} + + +char *grpc_serial_impl_serialize(serial_handle handle, value v, size_t *size) +{ + grpc_document document = static_cast(handle); + + if (handle == NULL || v == NULL || size == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Serialization called with wrong arguments in gRPC implementation"); + return NULL; + } + + /* Reset the embedded message before reuse */ + document->impl.Clear(); + + grpc_serial_impl_serialize_value(v, &document->impl); + + return grpc_serial_impl_document_stringify(document, size); +} + + +value grpc_serial_impl_deserialize_value(const google::protobuf::Value *pb_v) +{ + switch (pb_v->kind_case()) + { + case google::protobuf::Value::kNullValue: + { + return value_create_null(); + } + + case google::protobuf::Value::kBoolValue: + { + return value_create_bool(pb_v->bool_value() ? 1L : 0L); + } + + case google::protobuf::Value::kNumberValue: + { + double d = pb_v->number_value(); + + /* Prefer the narrowest integer type that fits, mirroring + * the RapidJSON deserialization heuristic. */ + if (d == static_cast(static_cast(d)) && + d >= static_cast(INT_MIN) && + d <= static_cast(INT_MAX)) + { + return value_create_int(static_cast(d)); + } + + if (d == static_cast(static_cast(d)) && + d >= static_cast(LONG_MIN) && + d <= static_cast(LONG_MAX)) + { + return value_create_long(static_cast(d)); + } + + /* Fallback: preserve as double */ + return value_create_double(d); + } + + case google::protobuf::Value::kStringValue: + { + const std::string &s = pb_v->string_value(); + return value_create_string(s.c_str(), s.size()); + } + + case google::protobuf::Value::kListValue: + { + const google::protobuf::ListValue &list = pb_v->list_value(); + int count = list.values_size(); + + value v_array = value_create_array(NULL, static_cast(count)); + + if (count == 0 || v_array == NULL) + { + return v_array; + } + + value *values = static_cast(value_to_array(v_array)); + + for (int i = 0; i < count; ++i) + { + values[i] = grpc_serial_impl_deserialize_value(&list.values(i)); + + if (values[i] == NULL) + { + for (int j = 0; j < i; ++j) + { + value_type_destroy(values[j]); + } + value_destroy(v_array); + return NULL; + } + } + + return v_array; + } + + case google::protobuf::Value::kStructValue: + { + const google::protobuf::Struct &st = pb_v->struct_value(); + const auto &fields = st.fields(); + size_t map_size = static_cast(fields.size()); + + value v_map = value_create_map(NULL, map_size); + + if (map_size == 0 || v_map == NULL) + { + return v_map; + } + + value *tuples = static_cast(value_to_map(v_map)); + size_t index = 0; + + for (auto it = fields.begin(); it != fields.end(); ++it) + { + value key_val = value_create_string(it->first.c_str(), it->first.size()); + value inner_val = grpc_serial_impl_deserialize_value(&it->second); + + if (key_val == NULL || inner_val == NULL) + { + if (key_val != NULL) + { + value_type_destroy(key_val); + } + if (inner_val != NULL) + { + value_type_destroy(inner_val); + } + value_type_destroy(v_map); + return NULL; + } + + const value tupla[] = { key_val, inner_val }; + tuples[index++] = value_create_array(tupla, sizeof(tupla) / sizeof(tupla[0])); + } + + return v_map; + } + + default: + { + break; + } + } + + log_write("metacall", LOG_LEVEL_ERROR, "Unsupported value kind in gRPC implementation"); + return NULL; +} + +/* ------------------------------------------------------------------------- + * Public: deserialize + * ---------------------------------------------------------------------- */ + +value grpc_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size) +{ + grpc_document document = static_cast(handle); + + if (handle == NULL || buffer == NULL || size == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Deserialization called with wrong arguments in gRPC implementation"); + return NULL; + } + + /* size includes a NUL sentinel byte added by stringify — exclude it */ + const size_t wire_size = size - 1; + + document->impl.Clear(); + + if (!document->impl.ParseFromArray(buffer, static_cast(wire_size))) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid parsing of Protobuf Value in gRPC implementation (%" PRIuS " bytes)", wire_size); + return NULL; + } + + return grpc_serial_impl_deserialize_value(&document->impl); +} + + +int grpc_serial_impl_destroy(serial_handle handle) +{ + grpc_document document = static_cast(handle); + + if (document != NULL) + { + delete document; + } + + return 0; +} From f9897c6b6414830bdec86931be1e388b02628bcb Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Tue, 31 Mar 2026 16:57:58 -0400 Subject: [PATCH 02/11] add grpc serial to cmakelists --- source/serials/CMakeLists.txt | 26 ++++++++------- source/serials/grpc_serial/CMakeLists.txt | 40 ++++++++++------------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/source/serials/CMakeLists.txt b/source/serials/CMakeLists.txt index 5fc2711dc5..ddb7217672 100644 --- a/source/serials/CMakeLists.txt +++ b/source/serials/CMakeLists.txt @@ -1,12 +1,14 @@ -# Check if serials are enabled -if(NOT OPTION_BUILD_SERIALS) - return() -endif() - -# Serial options -option(OPTION_BUILD_SERIALS_METACALL "MetaCall Native Format library serial." ON) -option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) - -# Serial packages -add_subdirectory(metacall_serial) # MetaCall Native Format library -add_subdirectory(rapid_json_serial) # RapidJSON library +# Check if serials are enabled +if(NOT OPTION_BUILD_SERIALS) + return() +endif() + +# Serial options +option(OPTION_BUILD_SERIALS_METACALL "MetaCall Native Format library serial." ON) +option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) +option(OPTION_BUILD_SERIALS_GRPC "gRPC serial." ON) + +# Serial packages +add_subdirectory(metacall_serial) +add_subdirectory(rapid_json_serial) +add_subdirectory(grpc_serial) diff --git a/source/serials/grpc_serial/CMakeLists.txt b/source/serials/grpc_serial/CMakeLists.txt index 2e8fddacc1..4085e05249 100644 --- a/source/serials/grpc_serial/CMakeLists.txt +++ b/source/serials/grpc_serial/CMakeLists.txt @@ -1,35 +1,37 @@ # Check if this serial is enabled -if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) +if(NOT OPTION_BUILD_SERIALS) + return() +endif() + +if(NOT OPTION_BUILD_SERIALS_GRPC) return() endif() # # External dependencies # +# Note: find gRPC only — it pulls in Protobuf automatically. +# Calling find_package(Protobuf) separately causes duplicate target errors. +# -find_package(Protobuf REQUIRED) find_package(gRPC QUIET) if(NOT gRPC_FOUND) - include(InstallGRPC) - if(NOT gRPC_FOUND) - message(SEND_ERROR "gRPC libraries not found") - return() - endif() - set(GRPC_INSTALL TRUE) + message(STATUS "gRPC not found, skipping grpc_serial") + return() endif() +# Resolve protobuf include dirs from the gRPC dependency +get_target_property(PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES) + # # Library name and options # -# Target name set(target grpc_serial) -# Exit here if required dependencies are not met message(STATUS "Serial ${target}") -# Set API export file and macro string(TOUPPER ${target} target_upper) set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -63,7 +65,6 @@ set(sources ${source_path}/grpc_serial_impl.cpp ) -# Group source files set(header_group "Header Files (API)") set(source_group "Source Files") source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" @@ -75,19 +76,15 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Create library # -# Build library add_library(${target} MODULE ${sources} ${headers} ) -# Create namespaced alias add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) -# Export library for downstream projects export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -# Create API export header generate_export_header(${target} EXPORT_FILE_NAME ${export_file} EXPORT_MACRO_NAME ${export_macro} @@ -113,8 +110,8 @@ target_include_directories(${target} ${PROJECT_BINARY_DIR}/source/include ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include - $ # MetaCall includes - ${Protobuf_INCLUDE_DIRS} # Protobuf includes + $ + ${PROTOBUF_INCLUDE_DIRS} PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} INTERFACE @@ -129,10 +126,9 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE - # On macOS, we use dynamic lookup to avoid loading libmetacall twice $<$>:${META_PROJECT_NAME}::metacall> - ${Protobuf_LIBRARIES} # libprotobuf - gRPC::grpc++ # libgrpc++ (brings in gRPC core) + protobuf::libprotobuf + gRPC::grpc++ PUBLIC ${DEFAULT_LIBRARIES} INTERFACE @@ -167,7 +163,6 @@ target_compile_options(${target} target_link_options(${target} PRIVATE - # On macOS, we use dynamic lookup for using existing symbols $<$,$>:-Wl,-undefined,dynamic_lookup> PUBLIC ${DEFAULT_LINKER_OPTIONS} @@ -178,7 +173,6 @@ target_link_options(${target} # Deployment # -# Library install(TARGETS ${target} EXPORT "${target}-export" COMPONENT dev RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime From eaaab48df17d6b52a42d1e324031882eee972c79 Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Tue, 31 Mar 2026 16:58:28 -0400 Subject: [PATCH 03/11] change grpc serial header files folder location --- .../include/grpc_serial/grpc_serial.h | 60 ++++++++++ .../include/grpc_serial/grpc_serial_impl.h | 108 ++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 source/serials/grpc_serial/include/grpc_serial/grpc_serial.h create mode 100644 source/serials/grpc_serial/include/grpc_serial/grpc_serial_impl.h diff --git a/source/serials/grpc_serial/include/grpc_serial/grpc_serial.h b/source/serials/grpc_serial/include/grpc_serial/grpc_serial.h new file mode 100644 index 0000000000..e25f5cabc3 --- /dev/null +++ b/source/serials/grpc_serial/include/grpc_serial/grpc_serial.h @@ -0,0 +1,60 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SERIAL_H +#define GRPC_SERIAL_H 1 + +/* -- Headers -- */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Methods -- */ + +/** +* @brief +* Instance of interface implementation +* +* @return +* Returns pointer to interface to be used by implementation +* +*/ +GRPC_SERIAL_API serial_interface grpc_serial_impl_interface_singleton(void); + +/** +* @brief +* Provide the module information +* +* @return +* Static string containing module information +* +*/ +GRPC_SERIAL_API const char *grpc_serial_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SERIAL_H */ diff --git a/source/serials/grpc_serial/include/grpc_serial/grpc_serial_impl.h b/source/serials/grpc_serial/include/grpc_serial/grpc_serial_impl.h new file mode 100644 index 0000000000..13aaeac832 --- /dev/null +++ b/source/serials/grpc_serial/include/grpc_serial/grpc_serial_impl.h @@ -0,0 +1,108 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SERIAL_IMPL_H +#define GRPC_SERIAL_IMPL_H 1 + +/* -- Headers -- */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Methods -- */ + +/** +* @brief +* Retrieve extension supported by gRPC implementation +* +* @return +* Returns constant string representing serial extension +* +*/ +GRPC_SERIAL_API const char *grpc_serial_impl_extension(void); + +/** +* @brief +* Initialize gRPC (Protobuf) serial document implementation +* +* @return +* Returns pointer to serial document implementation on success, null pointer otherwise +* +*/ +GRPC_SERIAL_API serial_handle grpc_serial_impl_initialize(memory_allocator allocator); + +/** +* @brief +* Serialize with gRPC (Protobuf) document implementation @handle +* +* @param[in] handle +* Pointer to the serial document implementation +* +* @param[in] v +* Reference to the value is going to be serialized +* +* @param[out] size +* Size in bytes of the return buffer +* +* @return +* String with the value serialized on correct serialization, null otherwise +* +*/ +GRPC_SERIAL_API char *grpc_serial_impl_serialize(serial_handle handle, value v, size_t *size); + +/** +* @brief +* Deserialize with gRPC (Protobuf) document implementation @handle +* +* @param[in] handle +* Pointer to the serial document implementation +* +* @param[in] buffer +* Reference to the string is going to be deserialized +* +* @param[in] size +* Size in bytes of the string @buffer +* +* @return +* Pointer to value deserialized on correct serialization, null otherwise +* +*/ +GRPC_SERIAL_API value grpc_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); + +/** +* @brief +* Destroy gRPC (Protobuf) document implementation +* +* @return +* Returns zero on correct destruction, distinct from zero otherwise +* +*/ +GRPC_SERIAL_API int grpc_serial_impl_destroy(serial_handle handle); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SERIAL_IMPL_H */ From 964dbe6f3f8ffe07de6b5e112d95a215e5a92464 Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Wed, 1 Apr 2026 23:26:57 -0400 Subject: [PATCH 04/11] add grpc serial tests --- source/tests/CMakeLists.txt | 1 + source/tests/grpc_serial_test/CMakeLists.txt | 171 +++++++++++ .../source/grpc_serial_test.cpp | 285 ++++++++++++++++++ source/tests/grpc_serial_test/source/main.cpp | 27 ++ 4 files changed, 484 insertions(+) create mode 100644 source/tests/grpc_serial_test/CMakeLists.txt create mode 100644 source/tests/grpc_serial_test/source/grpc_serial_test.cpp create mode 100644 source/tests/grpc_serial_test/source/main.cpp diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index b839796e6e..d12791d4dc 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -91,6 +91,7 @@ add_subdirectory(reflect_scope_test) add_subdirectory(reflect_metadata_test) add_subdirectory(dynlink_test) add_subdirectory(detour_test) +add_subdirectory(grpc_serial_test) add_subdirectory(serial_test) add_subdirectory(configuration_test) add_subdirectory(rb_loader_parser_test) diff --git a/source/tests/grpc_serial_test/CMakeLists.txt b/source/tests/grpc_serial_test/CMakeLists.txt new file mode 100644 index 0000000000..1974ad01d1 --- /dev/null +++ b/source/tests/grpc_serial_test/CMakeLists.txt @@ -0,0 +1,171 @@ +# Check if this serial test is enabled +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target grpc-serial-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/grpc_serial_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + grpc_serial + rapid_json_serial +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" +) diff --git a/source/tests/grpc_serial_test/source/grpc_serial_test.cpp b/source/tests/grpc_serial_test/source/grpc_serial_test.cpp new file mode 100644 index 0000000000..305617fec1 --- /dev/null +++ b/source/tests/grpc_serial_test/source/grpc_serial_test.cpp @@ -0,0 +1,285 @@ +#include + +#include +#include + +#include + +class grpc_serial_test : public testing::Test +{ +protected: + void SetUp() override + { + log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout)); + + allocator = memory_allocator_std(&malloc, &realloc, &free); + + ASSERT_EQ(0, serial_initialize()); + + s = serial_create("grpc"); + ASSERT_NE(nullptr, s); + } + + void TearDown() override + { + serial_clear(s); + serial_destroy(); + memory_allocator_destroy(allocator); + } + + serial s; + memory_allocator allocator; +}; + +// Positive and Negative integer tests + +TEST_F(grpc_serial_test, IntPositive) +{ + value v = value_create_int(42); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_INT, value_type_id(out)); + EXPECT_EQ(42, value_to_int(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +TEST_F(grpc_serial_test, IntNegative) +{ + value v = value_create_int(-123); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(-123, value_to_int(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// String tests + +TEST_F(grpc_serial_test, StringBasic) +{ + const char *msg = "hello grpc"; + + value v = value_create_string(msg, strlen(msg)); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_STRING, value_type_id(out)); + EXPECT_STREQ(msg, value_to_string(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +TEST_F(grpc_serial_test, StringEmpty) +{ + value v = value_create_string("", 0); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_STRING, value_type_id(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +TEST_F(grpc_serial_test, StringSpecialChars) +{ + const char *msg = "line1\nline2\t\"quoted\""; + + value v = value_create_string(msg, strlen(msg)); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_STREQ(msg, value_to_string(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// Bool and double tests + +TEST_F(grpc_serial_test, BoolTest) +{ + value v = value_create_bool(1L); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_BOOL, value_type_id(out)); + EXPECT_EQ(1L, value_to_bool(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +TEST_F(grpc_serial_test, DoubleTest) +{ + value v = value_create_double(3.14); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_DOUBLE, value_type_id(out)); + EXPECT_NEAR(3.14, value_to_double(out), 1e-6); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// Null test + +TEST_F(grpc_serial_test, NullTest) +{ + value v = value_create_null(); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_NULL, value_type_id(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// Array test + +TEST_F(grpc_serial_test, SimpleArray) +{ + const value arr[] = { + value_create_int(1), + value_create_int(2), + value_create_int(3) + }; + + value v = value_create_array(arr, 3); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_ARRAY, value_type_id(out)); + EXPECT_EQ(3, value_type_count(out)); + + value *vals = value_to_array(out); + EXPECT_EQ(1, value_to_int(vals[0])); + EXPECT_EQ(2, value_to_int(vals[1])); + EXPECT_EQ(3, value_to_int(vals[2])); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// Map test + +TEST_F(grpc_serial_test, SimpleMap) +{ + const value pair[] = { + value_create_string("key", 3), + value_create_int(99) + }; + + const value map_arr[] = { + value_create_array(pair, 2) + }; + + value v = value_create_map(map_arr, 1); + + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + ASSERT_NE(nullptr, buf); + + value out = serial_deserialize(s, buf, size, allocator); + + ASSERT_NE(nullptr, out); + EXPECT_EQ(TYPE_MAP, value_type_id(out)); + + memory_allocator_deallocate(allocator, buf); + value_destroy(v); + value_destroy(out); +} + +// Error case test + +TEST_F(grpc_serial_test, UnsupportedTypeFailsGracefully) +{ + // Example: function type (unsupported) + value v = value_create_null(); + size_t size = 0; + char *buf = serial_serialize(s, v, &size, allocator); + + // Should not crash + EXPECT_TRUE(buf == nullptr || size > 0); + + if (buf) + memory_allocator_deallocate(allocator, buf); + + value_destroy(v); +} \ No newline at end of file diff --git a/source/tests/grpc_serial_test/source/main.cpp b/source/tests/grpc_serial_test/source/main.cpp new file mode 100644 index 0000000000..43790d8fa7 --- /dev/null +++ b/source/tests/grpc_serial_test/source/main.cpp @@ -0,0 +1,27 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From 55a398335f299bf5b31f8c2ea7019fb3b886721b Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Thu, 2 Apr 2026 18:12:13 -0400 Subject: [PATCH 05/11] update CMake guard to use OPTION_BUILD_SERIALS_GRPC only --- source/tests/grpc_serial_test/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/tests/grpc_serial_test/CMakeLists.txt b/source/tests/grpc_serial_test/CMakeLists.txt index 1974ad01d1..0f4689e730 100644 --- a/source/tests/grpc_serial_test/CMakeLists.txt +++ b/source/tests/grpc_serial_test/CMakeLists.txt @@ -1,5 +1,4 @@ -# Check if this serial test is enabled -if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) +if(NOT OPTION_BUILD_SERIALS_GRPC) return() endif() From 594057a09de6d9b5afd1388f72007c3649033dcd Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Sun, 12 Apr 2026 09:21:04 -0400 Subject: [PATCH 06/11] Update CMakeLists.txt --- source/serials/grpc_serial/CMakeLists.txt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/source/serials/grpc_serial/CMakeLists.txt b/source/serials/grpc_serial/CMakeLists.txt index 4085e05249..666d6e1aa7 100644 --- a/source/serials/grpc_serial/CMakeLists.txt +++ b/source/serials/grpc_serial/CMakeLists.txt @@ -1,19 +1,13 @@ # Check if this serial is enabled -if(NOT OPTION_BUILD_SERIALS) - return() -endif() - -if(NOT OPTION_BUILD_SERIALS_GRPC) +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) return() endif() # # External dependencies # -# Note: find gRPC only — it pulls in Protobuf automatically. -# Calling find_package(Protobuf) separately causes duplicate target errors. -# +# gRPC and Protobuf find_package(gRPC QUIET) if(NOT gRPC_FOUND) From c777f85356c3743de92982ffbed3267c7d779a7d Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Sun, 12 Apr 2026 09:21:18 -0400 Subject: [PATCH 07/11] Update CMakeLists.txt --- source/serials/grpc_serial/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/serials/grpc_serial/CMakeLists.txt b/source/serials/grpc_serial/CMakeLists.txt index 666d6e1aa7..e0cf3e831c 100644 --- a/source/serials/grpc_serial/CMakeLists.txt +++ b/source/serials/grpc_serial/CMakeLists.txt @@ -8,7 +8,7 @@ endif() # # gRPC and Protobuf -find_package(gRPC QUIET) +find_package(gRPC) if(NOT gRPC_FOUND) message(STATUS "gRPC not found, skipping grpc_serial") From 679ff568d0622a14e16cc5e5af4161de3038661a Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Sun, 12 Apr 2026 09:22:50 -0400 Subject: [PATCH 08/11] Update CMakeLists.txt --- source/serials/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/serials/CMakeLists.txt b/source/serials/CMakeLists.txt index ddb7217672..bb4a2e466c 100644 --- a/source/serials/CMakeLists.txt +++ b/source/serials/CMakeLists.txt @@ -9,6 +9,6 @@ option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) option(OPTION_BUILD_SERIALS_GRPC "gRPC serial." ON) # Serial packages -add_subdirectory(metacall_serial) -add_subdirectory(rapid_json_serial) +add_subdirectory(metacall_serial) # MetaCall Native Format library +add_subdirectory(rapid_json_serial) # RapidJSON library add_subdirectory(grpc_serial) From 67f2f10bab137c337e56284c535c73e95616b304 Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Sun, 12 Apr 2026 09:24:47 -0400 Subject: [PATCH 09/11] Update CMakeLists.txt --- source/serials/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/serials/CMakeLists.txt b/source/serials/CMakeLists.txt index bb4a2e466c..7b332def50 100644 --- a/source/serials/CMakeLists.txt +++ b/source/serials/CMakeLists.txt @@ -6,9 +6,9 @@ endif() # Serial options option(OPTION_BUILD_SERIALS_METACALL "MetaCall Native Format library serial." ON) option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) -option(OPTION_BUILD_SERIALS_GRPC "gRPC serial." ON) +option(OPTION_BUILD_SERIALS_GRPC "gRPC library serial." ON) # Serial packages add_subdirectory(metacall_serial) # MetaCall Native Format library add_subdirectory(rapid_json_serial) # RapidJSON library -add_subdirectory(grpc_serial) +add_subdirectory(grpc_serial) # gRPC and Protobuf library From 5d12729d18f932d279d79fbb5c170594774e6e41 Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:41:12 -0400 Subject: [PATCH 10/11] Update CMakeLists.txt --- source/tests/grpc_serial_test/CMakeLists.txt | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/source/tests/grpc_serial_test/CMakeLists.txt b/source/tests/grpc_serial_test/CMakeLists.txt index 0f4689e730..6269e3c375 100644 --- a/source/tests/grpc_serial_test/CMakeLists.txt +++ b/source/tests/grpc_serial_test/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT OPTION_BUILD_SERIALS_GRPC) +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_GRPC) return() endif() @@ -72,20 +72,6 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include - - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ ) # @@ -150,8 +136,8 @@ add_test(NAME ${target} # add_dependencies(${target} + serial grpc_serial - rapid_json_serial ) # From cf4e87cd59fb1ef21d93ba5668c395d83ecd1b3b Mon Sep 17 00:00:00 2001 From: Eyouel Kibret Date: Thu, 16 Apr 2026 15:09:32 -0400 Subject: [PATCH 11/11] install required grpc libraries --- cmake/FindGRPC.cmake | 89 ++++++++++++++++++++ cmake/InstallGRPC.cmake | 67 +++++++++++++++ source/serials/grpc_serial/CMakeLists.txt | 70 +++------------ source/tests/grpc_serial_test/CMakeLists.txt | 80 ++++-------------- 4 files changed, 185 insertions(+), 121 deletions(-) create mode 100644 cmake/FindGRPC.cmake create mode 100644 cmake/InstallGRPC.cmake diff --git a/cmake/FindGRPC.cmake b/cmake/FindGRPC.cmake new file mode 100644 index 0000000000..634e93798d --- /dev/null +++ b/cmake/FindGRPC.cmake @@ -0,0 +1,89 @@ +# +# CMake Find gRPC by Parra Studios +# CMake script to find gRPC and Protobuf libraries. +# +# Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The following variables are set: +# +# GRPC_FOUND - TRUE if gRPC and Protobuf were found. +# GRPC_INCLUDE_DIRS - Protobuf include directories (contains google/protobuf/struct.pb.h). +# GRPC_LIBRARIES - Libraries to link against (protobuf + grpc++). + +# Prevent verbosity if already included +if(GRPC_FOUND) + set(GRPC_FIND_QUIETLY TRUE) +endif() + +# Try CMake config first (works with Homebrew, vcpkg, system installs) +find_package(gRPC QUIET CONFIG) + +if(gRPC_FOUND) + set(GRPC_FOUND TRUE) + set(GRPC_LIBRARIES gRPC::grpc++ protobuf::libprotobuf) + + get_target_property(GRPC_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES) + + if(NOT GRPC_FIND_QUIETLY) + message(STATUS "Found gRPC via CMake config") + endif() + + return() +endif() + +# Fallback: pkg-config +find_package(PkgConfig QUIET) + +if(PKG_CONFIG_FOUND) + pkg_check_modules(GRPC QUIET grpc++ protobuf) +endif() + +# Fallback: manual search +if(NOT GRPC_FOUND) + find_path(GRPC_INCLUDE_DIRS + NAMES google/protobuf/struct.pb.h + PATHS + /usr/local/include + /usr/include + /opt/homebrew/include + DOC "Include directory for Protobuf (required by grpc_serial)" + ) + + find_library(GRPC_GRPCPP_LIBRARY + NAMES grpc++ grpc++_unsecure + PATHS /usr/local/lib /usr/lib /opt/homebrew/lib + ) + + find_library(GRPC_PROTOBUF_LIBRARY + NAMES protobuf + PATHS /usr/local/lib /usr/lib /opt/homebrew/lib + ) + + if(GRPC_INCLUDE_DIRS AND GRPC_GRPCPP_LIBRARY AND GRPC_PROTOBUF_LIBRARY) + set(GRPC_FOUND TRUE) + set(GRPC_LIBRARIES ${GRPC_GRPCPP_LIBRARY} ${GRPC_PROTOBUF_LIBRARY}) + endif() +endif() + +mark_as_advanced(GRPC_FOUND GRPC_INCLUDE_DIRS GRPC_LIBRARIES) + +if(GRPC_FOUND) + if(NOT GRPC_FIND_QUIETLY) + message(STATUS "Found gRPC/Protobuf headers in ${GRPC_INCLUDE_DIRS}") + endif() +elseif(GRPC_FIND_REQUIRED) + message(FATAL_ERROR "Could not find gRPC/Protobuf") +endif() \ No newline at end of file diff --git a/cmake/InstallGRPC.cmake b/cmake/InstallGRPC.cmake new file mode 100644 index 0000000000..54b0deddc7 --- /dev/null +++ b/cmake/InstallGRPC.cmake @@ -0,0 +1,67 @@ +# +# CMake Install gRPC by Parra Studios +# CMake script to install gRPC and Protobuf via FetchContent. +# +# Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The following variables are set: +# +# GRPC_FOUND - TRUE if gRPC was installed successfully. +# GRPC_INCLUDE_DIRS - Include directories for Protobuf headers. +# GRPC_LIBRARIES - Libraries to link against. + +if(NOT GRPC_FOUND OR USE_BUNDLED_GRPC) + include(FetchContent) + + # Pin to a specific release tag for reproducibility + if(NOT GRPC_VERSION OR USE_BUNDLED_GRPC) + set(GRPC_VERSION v1.60.0) + endif() + + message(STATUS "Installing gRPC ${GRPC_VERSION} via FetchContent (this may take a while)") + + # Disable building unnecessary gRPC components to keep build times reasonable + set(FETCHCONTENT_QUIET OFF) + set(gRPC_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_CODEGEN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_CPP_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_CSHARP_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_NODE_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE) + set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE) + set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(ABSL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) + + FetchContent_Declare(gRPC + GIT_REPOSITORY https://github.com/grpc/grpc.git + GIT_TAG ${GRPC_VERSION} + GIT_SUBMODULES_RECURSE ON + ) + + FetchContent_MakeAvailable(gRPC) + + set(GRPC_FOUND TRUE) + set(GRPC_LIBRARIES gRPC::grpc++ protobuf::libprotobuf) + + get_target_property(GRPC_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES) + + mark_as_advanced(GRPC_INCLUDE_DIRS GRPC_LIBRARIES) + + message(STATUS "Installed gRPC ${GRPC_VERSION}") +endif() \ No newline at end of file diff --git a/source/serials/grpc_serial/CMakeLists.txt b/source/serials/grpc_serial/CMakeLists.txt index e0cf3e831c..b630ae9025 100644 --- a/source/serials/grpc_serial/CMakeLists.txt +++ b/source/serials/grpc_serial/CMakeLists.txt @@ -7,16 +7,18 @@ endif() # External dependencies # -# gRPC and Protobuf -find_package(gRPC) +find_package(GRPC) -if(NOT gRPC_FOUND) - message(STATUS "gRPC not found, skipping grpc_serial") - return() -endif() +if(NOT GRPC_FOUND) + include(InstallGRPC) -# Resolve protobuf include dirs from the gRPC dependency -get_target_property(PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES) + if(NOT GRPC_FOUND) + message(SEND_ERROR "gRPC libraries not found") + return() + endif() + + set(GRPC_INSTALL TRUE) +endif() # # Library name and options @@ -30,22 +32,9 @@ string(TOUPPER ${target} target_upper) set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") -# -# Compiler warnings -# - include(Warnings) - -# -# Compiler security -# - include(SecurityFlags) -# -# Sources -# - set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") @@ -66,10 +55,6 @@ source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" ${source_group} ${sources}) -# -# Create library -# - add_library(${target} MODULE ${sources} ${headers} @@ -84,10 +69,6 @@ generate_export_header(${target} EXPORT_MACRO_NAME ${export_macro} ) -# -# Project options -# - set_target_properties(${target} PROPERTIES ${DEFAULT_PROJECT_OPTIONS} @@ -95,17 +76,13 @@ set_target_properties(${target} BUNDLE $,$> ) -# -# Include directories -# - target_include_directories(${target} PRIVATE ${PROJECT_BINARY_DIR}/source/include ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include $ - ${PROTOBUF_INCLUDE_DIRS} + ${GRPC_INCLUDE_DIRS} PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} INTERFACE @@ -114,24 +91,15 @@ target_include_directories(${target} $ ) -# -# Libraries -# - target_link_libraries(${target} PRIVATE $<$>:${META_PROJECT_NAME}::metacall> - protobuf::libprotobuf - gRPC::grpc++ + ${GRPC_LIBRARIES} PUBLIC ${DEFAULT_LIBRARIES} INTERFACE ) -# -# Compile definitions -# - target_compile_definitions(${target} PRIVATE PUBLIC @@ -140,10 +108,6 @@ target_compile_definitions(${target} INTERFACE ) -# -# Compile options -# - target_compile_options(${target} PRIVATE PUBLIC @@ -151,10 +115,6 @@ target_compile_options(${target} INTERFACE ) -# -# Linker options -# - target_link_options(${target} PRIVATE $<$,$>:-Wl,-undefined,dynamic_lookup> @@ -163,13 +123,9 @@ target_link_options(${target} INTERFACE ) -# -# Deployment -# - install(TARGETS ${target} EXPORT "${target}-export" COMPONENT dev RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev -) +) \ No newline at end of file diff --git a/source/tests/grpc_serial_test/CMakeLists.txt b/source/tests/grpc_serial_test/CMakeLists.txt index 6269e3c375..c108378908 100644 --- a/source/tests/grpc_serial_test/CMakeLists.txt +++ b/source/tests/grpc_serial_test/CMakeLists.txt @@ -10,22 +10,9 @@ endif() set(target grpc-serial-test) message(STATUS "Test ${target}") -# -# Compiler warnings -# - include(Warnings) - -# -# Compiler security -# - include(SecurityFlags) -# -# Sources -# - set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") @@ -34,7 +21,6 @@ set(sources ${source_path}/grpc_serial_test.cpp ) -# Group source files set(header_group "Header Files (API)") set(source_group "Source Files") source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" @@ -42,108 +28,74 @@ source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" ${source_group} ${sources}) -# -# Create executable -# - -# Build executable add_executable(${target} ${sources} ) -# Create namespaced alias add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) -# -# Project options -# - set_target_properties(${target} PROPERTIES ${DEFAULT_PROJECT_OPTIONS} FOLDER "${IDE_FOLDER}" ) -# -# Include directories -# - target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include -) -# -# Libraries -# + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) target_link_libraries(${target} PRIVATE ${DEFAULT_LIBRARIES} - GTest - ${META_PROJECT_NAME}::metacall ) -# -# Compile definitions -# - target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} ) -# -# Compile options -# - target_compile_options(${target} PRIVATE ${DEFAULT_COMPILE_OPTIONS} ) -# -# Compile features -# - target_compile_features(${target} PRIVATE cxx_std_17 ) -# -# Linker options -# - target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) -# -# Define test -# - add_test(NAME ${target} COMMAND $ ) -# -# Define dependencies -# - add_dependencies(${target} - serial grpc_serial + rapid_json_serial ) -# -# Define test labels -# - set_property(TEST ${target} PROPERTY LABELS ${target} ) @@ -153,4 +105,4 @@ include(TestEnvironmentVariables) test_environment_variables(${target} "" "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" -) +) \ No newline at end of file