使用CMake FetchContent拉取Protobuf和gRPC并构建时编译(封闭构建)
问题
我正在撰写一篇gRPC相关博客,希望读者无需全局安装Protobuf和gRPC即可运行示例代码,因此尝试完全借助CMake的FetchContent下载、编译Protobuf和gRPC,并将其用于项目代码构建(用户仅需复制粘贴CMakeLists.txt)。但编译时出现大量错误,以下是我的CMakeLists.txt代码:
cmake_minimum_required(VERSION 3.15) project(command_server) include(FetchContent) FetchContent_Declare( absl GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git GIT_TAG origin/master OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(absl) FetchContent_Declare(Protobuf GIT_REPOSITORY git@github.com:protocolbuffers/protobuf.git GIT_TAG v23.3 SOURCE_SUBDIR cmake OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(Protobuf) FetchContent_Declare( gRPC GIT_REPOSITORY git@github.com:grpc/grpc GIT_TAG v1.55.1 # e.g v1.28.0 OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(gRPC) find_package(absl REQUIRED) FetchContent_GetProperties(Protobuf SOURCE_DIR Protobuf_SOURCE_DIR) include(${Protobuf_SOURCE_DIR}/cmake/protobuf-generate.cmake) add_library(proto OBJECT "${CMAKE_CURRENT_LIST_DIR}/helloworld.proto") target_link_libraries(proto PUBLIC protobuf::libprotobuf) set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") target_include_directories(proto PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>") protobuf_generate( TARGET proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=$<TARGET_FILE:grpc_cpp_plugin>" IMPORT_DIRS ${PROTO_IMPORT_DIRS} PROTOC_OUT_DIR "${PROTO_BINARY_DIR}") add_executable(my_exe greater_server.cc) target_link_libraries(my_exe grpc++ proto)
编译时出现如下错误:
CMake Warning at build/_deps/absl-src/CMakeLists.txt:77 (message): A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly. -- -- 23.1.0 CMake Deprecation Warning at build/_deps/grpc-src/third_party/zlib/CMakeLists.txt:1 (cmake_minimum_required): Compatibility with CMake < 2.8.12 will be removed from a future version of CMake. Update the VERSION argument <min> value or use a ...<max> suffix to tell CMake that the project does not need compatibility with older versions. -- Found systemd via pkg-config. -- Configuring done CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotobuf-lite" which requires target "absl_absl_check" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotobuf-lite" which requires target "absl_absl_log" that is not in any export set. ... snipped ... CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_memory" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_node_hash_map" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_node_hash_set" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_optional" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_span" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_status" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_statusor" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_strings" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_synchronization" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_time" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_type_traits" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_utility" that is not in any export set. CMake Error: install(EXPORT "protobuf-targets" ...) includes target "libprotoc" which requires target "absl_variant" that is not in any export set. CMake Error: install(EXPORT "utf8_range-targets" ...) includes target "utf8_validity" which requires target "absl_strings" that is not in any export set. -- Generating done CMake Generate step failed. Build files cannot be regenerated correctly.
解决思路
核心问题分析
错误本质是Protobuf导出目标时,依赖的Abseil组件未被正确纳入导出集合,同时存在版本不匹配、FetchContent加载顺序错误、依赖安装配置冲突等问题,具体修复步骤如下:
1. 统一依赖版本,消除兼容性冲突
- gRPC v1.55.1官方适配的Protobuf版本为v23.3(当前版本选择正确),但Abseil不能使用
origin/master分支,必须指定gRPC和Protobuf兼容的特定版本:gRPC v1.55.1对应的Abseil版本是20230125.3。 - 将Git仓库地址替换为HTTPS格式,避免用户需要配置SSH密钥才能克隆代码。
2. 调整FetchContent加载顺序
gRPC内部依赖Protobuf和Abseil,正确加载顺序应为:先加载Abseil,再加载Protobuf,最后加载gRPC。同时:
- 无需手动调用
find_package(absl REQUIRED),FetchContent_MakeAvailable会自动完成依赖配置。 - 移除
OVERRIDE_FIND_PACKAGE选项,对于博客示例,直接让FetchContent管理所有依赖更稳妥,避免覆盖系统已有包引发冲突。
3. 配置Abseil的C++标准传播
根据Abseil的警告提示,设置ABSL_PROPAGATE_CXX_STD为ON,确保项目与依赖使用统一的C++标准,避免编译错误。
4. 禁用Protobuf的安装导出(关键修复)
错误根源是Protobuf默认生成安装导出目标,但这些目标依赖的Abseil组件未被包含在导出集中。对于仅本地构建使用的依赖,无需安装,因此禁用相关选项:
- 设置
protobuf_INSTALL为OFF,跳过安装步骤,避免导出目标的问题。 - 设置
protobuf_BUILD_TESTS为OFF,减少编译时间和磁盘占用。 - 可选设置
protobuf_BUILD_SHARED_LIBS控制是否生成动态库。
5. 简化protobuf_generate用法
gRPC和Protobuf已提供成熟的代码生成宏,无需手动导入cmake模块:
- 无需手动包含
protobuf-generate.cmake,FetchContent_MakeAvailable(Protobuf)会自动导入相关模块。 - 明确分离Protobuf和gRPC代码生成步骤,确保目标配置正确。
修改后的完整CMakeLists.txt
cmake_minimum_required(VERSION 3.15) project(command_server) # 设置统一C++标准,适配依赖要求 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) include(FetchContent) # 1. 加载Abseil,使用gRPC v1.55.1兼容的版本 FetchContent_Declare( absl GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git GIT_TAG 20230125.3 ) # 启用C++标准传播,确保依赖与项目标准一致 set(ABSL_PROPAGATE_CXX_STD ON) FetchContent_MakeAvailable(absl) # 2. 加载Protobuf,禁用安装和测试 FetchContent_Declare( Protobuf GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git GIT_TAG v23.3 SOURCE_SUBDIR cmake ) set(protobuf_BUILD_SHARED_LIBS OFF) set(protobuf_INSTALL OFF) set(protobuf_BUILD_TESTS OFF) FetchContent_MakeAvailable(Protobuf) # 3. 加载gRPC,禁用测试和安装 FetchContent_Declare( gRPC GIT_REPOSITORY https://github.com/grpc/grpc.git GIT_TAG v1.55.1 ) set(gRPC_BUILD_TESTS OFF) set(gRPC_INSTALL OFF) FetchContent_MakeAvailable(gRPC) # 获取gRPC C++插件路径 get_target_property(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin LOCATION) # 配置proto文件和生成目录 set(PROTO_FILE "${CMAKE_CURRENT_LIST_DIR}/helloworld.proto") set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") # 生成Protobuf C++代码 protobuf_generate( TARGET proto LANGUAGE cpp OUT_VAR PROTO_SOURCES OUT_DIR ${PROTO_BINARY_DIR} PROTOS ${PROTO_FILE} ) # 生成gRPC C++代码 protobuf_generate( TARGET proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${GRPC_CPP_PLUGIN_EXECUTABLE}" OUT_DIR ${PROTO_BINARY_DIR} PROTOS ${PROTO_FILE} ) # 创建proto目标,链接依赖库 add_library(proto OBJECT ${PROTO_SOURCES}) target_link_libraries(proto PUBLIC protobuf::libprotobuf grpc++ ) target_include_directories(proto PUBLIC $<BUILD_INTERFACE:${PROTO_BINARY_DIR}> ) # 构建可执行文件,链接依赖 add_executable(my_exe greater_server.cc) target_link_libraries(my_exe PRIVATE proto grpc++ grpc++_reflection # 可选:添加反射支持,方便调试 )
额外说明
- 添加
grpc++_reflection链接,支持服务端反射功能(可根据示例需求移除)。 - 明确设置C++17标准,这是gRPC和Abseil推荐的最低兼容标准。
- 禁用所有依赖的测试和安装,大幅减少编译时间和磁盘占用。
- 替换SSH仓库地址为HTTPS,降低用户环境配置门槛。
内容的提问来源于stack exchange,提问作者Tyler
相关产品推荐
相关产品推荐

