| 1 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | # Macros and functions for handling of Protobuf |
| 3 | |
| 4 | # Prepare a vendored copy of Protobuf for use with Netdata. |
| 5 | function(netdata_bundle_protobuf) |
| 6 | include(FetchContent) |
| 7 | include(NetdataFetchContentExtra) |
| 8 | |
| 9 | set(PROTOBUF_TAG f0dc78d7e6e331b8c6bb2d5283e06aa26883ca7c) # v21.12 |
| 10 | set(NEED_ABSL False) |
| 11 | |
| 12 | if(CMAKE_CXX_STANDARD GREATER_EQUAL 17) |
| 13 | set(PROTOBUF_TAG edaa823d8b36a8656d7b2b9241b7d0bfe50af878) # v33.4 |
| 14 | set(NEED_ABSL True) |
| 15 | set(ABSL_TAG d407ef122a08203648451e0fec77b3f868b71112) # 20260107.0 |
| 16 | endif() |
| 17 | |
| 18 | set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE NEVER) |
| 19 | |
| 20 | string(REPLACE "-fsanitize=address" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") |
| 21 | string(REPLACE "-fsanitize=address" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") |
| 22 | |
| 23 | # ignore debhelper |
| 24 | set(FETCHCONTENT_FULLY_DISCONNECTED Off) |
| 25 | |
| 26 | if(NEED_ABSL) |
| 27 | set(ABSL_PROPAGATE_CXX_STD On) |
| 28 | set(ABSL_ENABLE_INSTALL Off) |
| 29 | set(BUILD_SHARED_LIBS Off) |
| 30 | set(ABSL_BUILD_TESTING Off) |
| 31 | set(absl_SOURCE_DIR "${CMAKE_BINARY_DIR}/_deps/absl-src") |
| 32 | set(absl_repo https://github.com/abseil/abseil-cpp) |
| 33 | |
| 34 | message(STATUS "Preparing bundled Abseil (required by bundled Protobuf)") |
| 35 | find_program(PATCH patch REQUIRED) |
| 36 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) |
| 37 | FetchContent_Declare(absl |
| 38 | GIT_REPOSITORY ${absl_repo} |
| 39 | GIT_TAG ${ABSL_TAG} |
| 40 | SOURCE_DIR ${absl_SOURCE_DIR} |
| 41 | PATCH_COMMAND ${CMAKE_SOURCE_DIR}/packaging/cmake/patches/apply-patches.sh |
| 42 | ${absl_SOURCE_DIR} |
| 43 | ${CMAKE_SOURCE_DIR}/packaging/cmake/patches/abseil |
| 44 | CMAKE_ARGS ${NETDATA_CMAKE_PROPAGATE_TOOLCHAIN_ARGS} |
| 45 | EXCLUDE_FROM_ALL |
| 46 | ) |
| 47 | else() |
| 48 | FetchContent_Declare(absl |
| 49 | GIT_REPOSITORY ${absl_repo} |
| 50 | GIT_TAG ${ABSL_TAG} |
| 51 | SOURCE_DIR ${absl_SOURCE_DIR} |
| 52 | PATCH_COMMAND ${CMAKE_SOURCE_DIR}/packaging/cmake/patches/apply-patches.sh |
| 53 | ${absl_SOURCE_DIR} |
| 54 | ${CMAKE_SOURCE_DIR}/packaging/cmake/patches/abseil |
| 55 | CMAKE_ARGS ${NETDATA_CMAKE_PROPAGATE_TOOLCHAIN_ARGS} |
| 56 | ) |
| 57 | endif() |
| 58 | FetchContent_MakeAvailable_NoInstall(absl) |
| 59 | message(STATUS "Finished preparing bundled Abseil") |
| 60 | endif() |
| 61 | |
| 62 | set(protobuf_INSTALL Off) |
| 63 | set(protobuf_BUILD_LIBPROTOC Off) |
| 64 | set(protobuf_BUILD_TESTS Off) |
| 65 | set(protobuf_BUILD_SHARED_LIBS Off) |
| 66 | set(protobuf_repo https://github.com/protocolbuffers/protobuf) |
| 67 | |
| 68 | message(STATUS "Preparing bundled Protobuf") |
| 69 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) |
| 70 | FetchContent_Declare(protobuf |
| 71 | GIT_REPOSITORY ${protobuf_repo} |
| 72 | GIT_TAG ${PROTOBUF_TAG} |
| 73 | CMAKE_ARGS ${NETDATA_CMAKE_PROPAGATE_TOOLCHAIN_ARGS} |
| 74 | EXCLUDE_FROM_ALL |
| 75 | ) |
| 76 | else() |
| 77 | FetchContent_Declare(protobuf |
| 78 | GIT_REPOSITORY ${protobuf_repo} |
| 79 | GIT_TAG ${PROTOBUF_TAG} |
| 80 | CMAKE_ARGS ${NETDATA_CMAKE_PROPAGATE_TOOLCHAIN_ARGS} |
| 81 | ) |
| 82 | endif() |
| 83 | FetchContent_MakeAvailable_NoInstall(protobuf) |
| 84 | message(STATUS "Finished preparing bundled Protobuf.") |
| 85 | |
| 86 | set(ENABLE_BUNDLED_PROTOBUF True PARENT_SCOPE) |
| 87 | endfunction() |
| 88 | |
| 89 | # Handle detection of Protobuf |
| 90 | macro(netdata_detect_protobuf) |
| 91 | if(OS_WINDOWS) |
| 92 | set(PROTOBUF_PROTOC_EXECUTABLE "$ENV{PROTOBUF_PROTOC_EXECUTABLE}") |
| 93 | if(NOT PROTOBUF_PROTOC_EXECUTABLE) |
| 94 | set(PROTOBUF_PROTOC_EXECUTABLE "/bin/protoc") |
| 95 | endif() |
| 96 | set(PROTOBUF_CFLAGS_OTHER "") |
| 97 | set(PROTOBUF_INCLUDE_DIRS "") |
| 98 | set(PROTOBUF_LIBRARIES "-lprotobuf") |
| 99 | |
| 100 | set(ENABLE_PROTOBUF True) |
| 101 | set(HAVE_PROTOBUF True) |
| 102 | else() |
| 103 | if(NOT ENABLE_BUNDLED_PROTOBUF) |
| 104 | if (NOT BUILD_SHARED_LIBS) |
| 105 | set(Protobuf_USE_STATIC_LIBS On) |
| 106 | endif() |
| 107 | |
| 108 | # The FindProtobuf CMake module shipped by upstream CMake is |
| 109 | # broken for Protobuf version 22.0 and newer because it does |
| 110 | # not correctly pull in the new Abseil dependencies. Protobuf |
| 111 | # itself sometimes ships a CMake Package Configuration module |
| 112 | # that _does_ work correctly, so use that in preference to the |
| 113 | # Find module shipped with CMake. |
| 114 | # |
| 115 | # The code below works by first attempting to use find_package |
| 116 | # in config mode, and then checking for the existence of the |
| 117 | # target we actually use that gets defined by the protobuf |
| 118 | # CMake Package Configuration Module to determine if that |
| 119 | # worked. A bit of extra logic is required in the case of the |
| 120 | # config mode working, because some systems ship compatibility |
| 121 | # logic for the old FindProtobuf module while others do not. |
| 122 | # |
| 123 | # Upstream bug reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24321 |
| 124 | find_package(Protobuf CONFIG) |
| 125 | |
| 126 | if(NOT TARGET protobuf::libprotobuf) |
| 127 | message(STATUS "Could not find Protobuf using Config mode, falling back to Module mode") |
| 128 | find_package(Protobuf REQUIRED) |
| 129 | endif() |
| 130 | endif() |
| 131 | |
| 132 | if(TARGET protobuf::libprotobuf) |
| 133 | get_property(IMPORTED_SET TARGET protobuf::libprotobuf PROPERTY IMPORTED_LOCATION SET) |
| 134 | get_property(ALIASED TARGET protobuf::libprotobuf PROPERTY ALIASED_TARGET) |
| 135 | |
| 136 | if(ALIASED STREQUAL "" AND NOT IMPORTED_SET) |
| 137 | set_property(TARGET protobuf::libprotobuf PROPERTY IMPORTED_LOCATION "${Protobuf_LIBRARY}") |
| 138 | endif() |
| 139 | |
| 140 | if(NOT Protobuf_PROTOC_EXECUTABLE AND TARGET protobuf::protoc) |
| 141 | set(Protobuf_PROTOC_EXECUTABLE protobuf::protoc) |
| 142 | endif() |
| 143 | |
| 144 | # It is technically possible that this may still not |
| 145 | # be set by this point, so we need to check it and |
| 146 | # fail noisily if it isn't because the build won't |
| 147 | # work without it. |
| 148 | if(NOT Protobuf_PROTOC_EXECUTABLE) |
| 149 | message(FATAL_ERROR "Could not determine the location of the protobuf compiler for the detected version of protobuf.") |
| 150 | endif() |
| 151 | |
| 152 | set(PROTOBUF_PROTOC_EXECUTABLE ${Protobuf_PROTOC_EXECUTABLE}) |
| 153 | set(PROTOBUF_LIBRARIES protobuf::libprotobuf) |
| 154 | endif() |
| 155 | |
| 156 | set(ENABLE_PROTOBUF True) |
| 157 | set(HAVE_PROTOBUF True) |
| 158 | endif() |
| 159 | endmacro() |
| 160 | |
| 161 | # Helper function to compile protocol definitions into C++ code. |
| 162 | function(netdata_protoc_generate_cpp PROTO_ROOT_DIR OUTPUT_ROOT_DIR GENERATED_SOURCES GENERATED_HEADERS) |
| 163 | if(NOT ARGN) |
| 164 | message(SEND_ERROR "Error: netdata_protoc_generate_cpp() called without any proto files") |
| 165 | return() |
| 166 | endif() |
| 167 | |
| 168 | # Initialize output variables |
| 169 | set(output_sources) |
| 170 | set(output_headers) |
| 171 | |
| 172 | # Setup include paths for protoc |
| 173 | set(protoc_include_paths ${PROTO_ROOT_DIR}) |
| 174 | if(ENABLE_BUNDLED_PROTOBUF) |
| 175 | list(APPEND protoc_include_paths ${CMAKE_BINARY_DIR}/_deps/protobuf-src/src/) |
| 176 | endif() |
| 177 | |
| 178 | # Process each proto file |
| 179 | foreach(proto_file ${ARGN}) |
| 180 | # Get absolute paths and component parts |
| 181 | get_filename_component(proto_file_abs_path ${proto_file} ABSOLUTE) |
| 182 | get_filename_component(proto_file_name_no_ext ${proto_file} NAME_WE) |
| 183 | get_filename_component(proto_file_dir ${proto_file} DIRECTORY) |
| 184 | |
| 185 | # Calculate relative output path to maintain directory structure |
| 186 | get_filename_component(proto_root_abs_path ${PROTO_ROOT_DIR} ABSOLUTE) |
| 187 | get_filename_component(proto_dir_abs_path ${proto_file_dir} ABSOLUTE) |
| 188 | file(RELATIVE_PATH proto_relative_path ${proto_root_abs_path} ${proto_dir_abs_path}) |
| 189 | |
| 190 | # Construct output file paths |
| 191 | set(output_dir "${OUTPUT_ROOT_DIR}/${proto_relative_path}") |
| 192 | set(generated_source "${output_dir}/${proto_file_name_no_ext}.pb.cc") |
| 193 | set(generated_header "${output_dir}/${proto_file_name_no_ext}.pb.h") |
| 194 | |
| 195 | # Add to output lists |
| 196 | list(APPEND output_sources "${generated_source}") |
| 197 | list(APPEND output_headers "${generated_header}") |
| 198 | |
| 199 | # Create custom command to generate the protobuf files |
| 200 | add_custom_command( |
| 201 | OUTPUT "${generated_source}" "${generated_header}" |
| 202 | COMMAND ${CMAKE_COMMAND} -E make_directory "${output_dir}" |
| 203 | COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} |
| 204 | ARGS "-I$<JOIN:${protoc_include_paths},;-I>" |
| 205 | --cpp_out=${OUTPUT_ROOT_DIR} |
| 206 | ${proto_file_abs_path} |
| 207 | DEPENDS ${proto_file_abs_path} ${PROTOBUF_PROTOC_EXECUTABLE} |
| 208 | COMMENT "Generating C++ protocol buffer files from ${proto_file}" |
| 209 | COMMAND_EXPAND_LISTS |
| 210 | VERBATIM |
| 211 | ) |
| 212 | endforeach() |
| 213 | |
| 214 | # Mark generated files with proper properties |
| 215 | set_source_files_properties( |
| 216 | ${output_sources} ${output_headers} |
| 217 | PROPERTIES |
| 218 | GENERATED TRUE |
| 219 | COMPILE_OPTIONS -Wno-deprecated-declarations |
| 220 | ) |
| 221 | |
| 222 | # Set output variables in parent scope |
| 223 | set(${GENERATED_SOURCES} ${output_sources} PARENT_SCOPE) |
| 224 | set(${GENERATED_HEADERS} ${output_headers} PARENT_SCOPE) |
| 225 | endfunction() |
| 226 | |
| 227 | # Add protobuf to a specified target. |
| 228 | function(netdata_add_protobuf _target) |
| 229 | if(ENABLE_BUNDLED_PROTOBUF) |
| 230 | target_include_directories(${_target} BEFORE PRIVATE ${PROTOBUF_INCLUDE_DIRS}) |
| 231 | else() |
| 232 | target_include_directories(${_target} PRIVATE ${PROTOBUF_INCLUDE_DIRS}) |
| 233 | endif() |
| 234 | |
| 235 | target_compile_options(${_target} PRIVATE ${PROTOBUF_CFLAGS_OTHER}) |
| 236 | target_link_libraries(${_target} PRIVATE ${PROTOBUF_LIBRARIES}) |
| 237 | endfunction() |