如何在CMake构建过程中生成protoc和grpc_cpp_plugin并用于生成gRPC/Protobuf代码
你说得太对了,硬编码本地绝对路径确实是个头疼的坏习惯,完全把构建过程绑死在你的机器上,换台环境直接就炸。其实咱们完全可以借助CMake的特性,让构建系统自动编译出protoc和grpc_cpp_plugin,再用这些本地构建的工具生成代码,全程不用写死任何路径。下面我给你一步步拆解怎么做:
核心思路
当你用FetchContent引入gRPC时,它会自动拉取并构建依赖的Protobuf库(包括protoc编译器);同时gRPC本身在构建时也会生成grpc_cpp_plugin插件。CMake会帮我们管理这些工具的构建路径,我们只需要用正确的方式引用它们就行,不用自己硬编码。
完整的CMake配置示例
我把你的代码修改成了符合要求的版本,关键修改点会标出来:
# 1. 引入依赖:保留你原来的FetchContent配置,补充工具构建选项 FetchContent_Declare( abseil GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git GIT_TAG 20250512.1 ) FetchContent_Declare( gRPC GIT_REPOSITORY https://github.com/grpc/grpc.git GIT_TAG v1.74.1 ) # 显式开启protoc和grpc_cpp_plugin的构建(gRPC默认会开启,但显式设置更稳妥) set(gRPC_BUILD_CPP_PLUGIN ON CACHE BOOL "Build gRPC C++ code generation plugin" FORCE) set(protobuf_BUILD_PROTOC_BINARIES ON CACHE BOOL "Build protoc compiler" FORCE) FetchContent_MakeAvailable(abseil gRPC) # 2. 定义proto文件与生成文件路径(优化:把生成文件放到二进制目录,避免污染源码) file(GLOB proto_files "${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto") # 生成文件存到构建目录下的子目录,不污染源码仓库 set(proto_gen_dir "${CMAKE_CURRENT_BINARY_DIR}/proto_generated") file(MAKE_DIRECTORY ${proto_gen_dir}) # 确保目录提前创建 set(proto_srcs "${proto_gen_dir}/test.pb.cc") set(proto_hdrs "${proto_gen_dir}/test.pb.h") set(grpc_srcs "${proto_gen_dir}/test.grpc.pb.cc") set(grpc_hdrs "${proto_gen_dir}/test.grpc.pb.h") # 3. 自动获取构建好的工具路径(重点!完全替代硬编码) # 用生成器表达式获取目标文件路径,CMake会在构建阶段自动解析实际路径 set(protoc_path $<TARGET_FILE:protoc>) set(grpc_plugin_path $<TARGET_FILE:grpc_cpp_plugin>) # 4. 修改自定义命令,使用自动获取的工具并添加依赖 add_custom_command( OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}" COMMAND ${protoc_path} ARGS --grpc_out "${proto_gen_dir}" --cpp_out "${proto_gen_dir}" -I "${CMAKE_CURRENT_SOURCE_DIR}/proto" --plugin=protoc-gen-grpc="${grpc_plugin_path}" "${proto_files}" DEPENDS ${proto_files} protoc # 确保protoc先编译完成再生成代码 grpc_cpp_plugin # 确保grpc插件先编译完成 COMMENT "Generating gRPC/Protobuf code from .proto files..." )
关键修改点说明
显式启用工具构建:
虽然gRPC默认会构建protoc和cpp插件,但显式设置gRPC_BUILD_CPP_PLUGIN和protobuf_BUILD_PROTOC_BINARIES为ON,能避免不同环境下的默认配置差异,让构建更稳定。生成文件与源码分离:
把生成的.pb.cc、.pb.h放到构建目录(CMAKE_CURRENT_BINARY_DIR)下的子目录,不会污染你的源码仓库,还能避免Debug/Release等不同构建配置的生成文件互相覆盖。自动解析工具路径:
用$<TARGET_FILE:protoc>和$<TARGET_FILE:grpc_cpp_plugin>这两个生成器表达式,CMake会在构建阶段自动解析出工具的实际路径,完全摆脱硬编码的束缚。添加工具依赖:
在DEPENDS里加上protoc和grpc_cpp_plugin目标,CMake会严格保证:先编译好这两个工具,再运行代码生成命令,不会出现“工具还没编译就想用”的错误。
后续衔接:把生成代码加入项目
当你把生成的源文件添加到可执行文件或库时,记得把生成目录加入头文件搜索路径:
add_executable(your_service main.cpp ${proto_srcs} ${grpc_srcs}) # 让编译器能找到生成的头文件 target_include_directories(your_service PRIVATE ${proto_gen_dir}) # 链接gRPC和Protobuf依赖库 target_link_libraries(your_service PRIVATE grpc++ grpc protobuf::libprotobuf)
这样整个构建流程就完全脱离了本地环境的硬编码路径,在任何机器上只要能拉取依赖源码,就能自动完成工具构建、代码生成和项目编译。
内容来源于stack exchange

