如何修复拆分Proto目录后的CMake+Protobuf项目构建失败问题
Protobuf + CMake多目录构建失败问题解决
问题描述
将Protobuf文件移至单独的proto目录后,CMake构建失败,提示找不到生成的message.pb.cc文件,且build/proto/generated目录为空,protobuf_generate_cpp未生成文件但无任何错误或警告。
项目结构
protobuf-example/ proto/ CMakeLists.txt message.proto CMakeLists.txt main.cpp
各文件原始内容
proto/CMakeLists.txt
set(PROTO_FILES message.proto) set(GENERATED_PROTO_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${GENERATED_PROTO_DIR}) protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES}) add_library(proto_files STATIC ${PROTO_SRCS}) target_include_directories(proto_files PUBLIC ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(proto_files PUBLIC ${Protobuf_LIBRARIES}) set(PROTO_GEN_SRCS ${PROTO_SRCS} PARENT_SCOPE) set(PROTO_GEN_HDRS ${PROTO_HDRS} PARENT_SCOPE) set(PROTO_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
message.proto
syntax = "proto3"; message Person { string name = 1; int32 age = 2; string email = 3; }
主CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(ProtobufExample LANGUAGES CXX) find_package(Protobuf REQUIRED) add_subdirectory(proto) add_executable(protobuf_example main.cpp ${PROTO_GEN_SRCS}) target_include_directories(protobuf_example PRIVATE ${Protobuf_INCLUDE_DIR}) target_link_libraries(protobuf_example PRIVATE ${proto_files})
main.cpp
#include <iostream> #include <fstream> #include "message.pb.h" void serializePerson(const std::string& filename) { Person person; person.set_name("John Doe"); person.set_age(30); person.set_email("john.doe@example.com"); std::ofstream output(filename, std::ios::binary); if (!person.SerializeToOstream(&output)) { std::cerr << "Failed to serialize data." << std::endl; } } void deserializePerson(const std::string& filename) { Person person; std::ifstream input(filename, std::ios::binary); if (!person.ParseFromIstream(&input)) { std::cerr << "Failed to parse data." << std::endl; } else { std::cout << "Name: " << person.name() << "\n" << "Age: " << person.age() << "\n" << "Email: " << person.email() << std::endl; } } int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; const std::string filename = "person.data"; serializePerson(filename); deserializePerson(filename); google::protobuf::ShutdownProtobufLibrary(); return 0; }
错误信息
Cannot find source file: /home/user/cmake-protobuf-test/protobuf-example/build/proto/message.pb.cc
问题根源
protobuf_generate_cpp默认将文件生成到当前CMakeLists.txt的二进制根目录,但代码中指定了GENERATED_PROTO_DIR却未告知函数使用该目录,导致生成路径与预期不符。- 主CMakeLists.txt中重复添加了
${PROTO_GEN_SRCS},而源文件已通过proto_files库管理,重复添加会导致CMake寻找不存在的路径文件。 - 主CMakeLists中
target_include_directories使用了错误的变量名${Protobuf_INCLUDE_DIR},正确应为${Protobuf_INCLUDE_DIRS}。
修正方案
1. 更新proto/CMakeLists.txt
调整protobuf_generate_cpp调用,明确指定输出目录,同时移除不必要的父作用域变量传递:
set(PROTO_FILES message.proto) set(GENERATED_PROTO_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${GENERATED_PROTO_DIR}) # 指定生成文件的输出目录 protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS OUT_DIR ${GENERATED_PROTO_DIR} ${PROTO_FILES} ) add_library(proto_files STATIC ${PROTO_SRCS}) # 让依赖库的目标能访问到生成的头文件和Protobuf头文件 target_include_directories(proto_files PUBLIC ${Protobuf_INCLUDE_DIRS} ${GENERATED_PROTO_DIR} ) target_link_libraries(proto_files PUBLIC ${Protobuf_LIBRARIES})
2. 更新主CMakeLists.txt
移除重复的源文件引用,修正变量名,简化依赖配置:
cmake_minimum_required(VERSION 3.10) project(ProtobufExample LANGUAGES CXX) find_package(Protobuf REQUIRED) add_subdirectory(proto) # 无需添加${PROTO_GEN_SRCS},源文件已包含在proto_files库中 add_executable(protobuf_example main.cpp) # 无需单独配置include目录,proto_files已通过PUBLIC暴露必要路径 target_link_libraries(protobuf_example PRIVATE proto_files)
3. 重新构建
清理旧构建目录后重新执行:
rm -rf build && mkdir build && cd build cmake .. make
此时message.pb.cc和message.pb.h会正确生成到build/proto/generated目录,项目可正常编译运行。
内容的提问来源于stack exchange,提问作者user2138149
相关产品推荐
相关产品推荐

