跨目录导入Protobuf文件并通过CMake构建失败问题排查
跨目录Protobuf+CMake构建错误修复方案
问题背景
项目中Protobuf消息分目录存放,msg_b.proto导入msg_a.proto复用message A作为字段,但CMake构建时出现编译错误,提示找不到Protobuf内部生成的符号。
项目目录结构
proto |-- CMakeLists.txt `-- src |-- CMakeLists.txt |-- msg_a | |-- CMakeLists.txt | `-- msg_a.proto `-- msg_b |-- CMakeLists.txt `-- msg_b.proto
各文件原始内容
顶层CMakeLists.txt
project(proto) cmake_minimum_required(VERSION 3.18) find_package(Protobuf REQUIRED) add_subdirectory(src)
proto/src/CMakeLists.txt
add_subdirectory(msg_a) add_subdirectory(msg_b)
msg_a/CMakeLists.txt
protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS msg_a.proto) add_library(msg_a ${PROTO_HDRS} ${PROTO_SRCS}) target_link_libraries(msg_a protobuf::libprotobuf)
msg_b/CMakeLists.txt
set(Protobuf_IMPORT_DIRS ${CMAKE_SOURCE_DIR}) protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS msg_b.proto) add_library(msg_b ${PROTO_HDRS} ${PROTO_SRCS}) target_include_directories(msg_b PUBLIC ${CMAKE_BINARY_DIR}) target_link_libraries(msg_b protobuf::libprotobuf)
msg_a.proto
syntax = "proto2"; package dummy.package; message A { optional int32 a = 1; optional int32 b = 2; optional double c = 3; }
msg_b.proto
syntax = "proto2"; package dummy.package.msb_b; import "src/msg_a/msg_a.proto"; message B { optional dummy.package.A a = 1; }
编译错误信息
[build] [ 50%] Building CXX object src/msg_a/CMakeFiles/msg_a.dir/msg_a.pb.cc.o [build] [ 66%] Building CXX object src/msg_b/CMakeFiles/msg_b.dir/msg_b.pb.cc.o [build] /home/aobrien/dev/cpp/proto/build/src/msg_b/msg_b.pb.cc:17:8: error: use of undeclared identifier 'PROTOBUF_INTERNAL_EXPORT_src_2fmsg_5fa_2fmsg_5fa_2eproto' [build] extern PROTOBUF_INTERNAL_EXPORT_src_2fmsg_5fa_2fmsg_5fa_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_A_src_2fmsg_5fa_2fmsg_5fa_2eproto; [build] ^ [build] /home/aobrien/dev/cpp/proto/build/src/msg_b/msg_b.pb.cc:70:6: error: no member named 'descriptor_table_src_2fmsg_5fa_2fmsg_5fa_2eproto' in the global namespace; did you mean 'descriptor_table_protodef_msg_5fb_2eproto'? [build] &::descriptor_table_src_2fmsg_5fa_2fmsg_5fa_2eproto, [build] ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [build] descriptor_table_protodef_msg_5fb_2eproto [build] /home/aobrien/dev/cpp/proto/build/src/msg_b/msg_b.pb.cc:64:12: note: 'descriptor_table_protodef_msg_5fb_2eproto' declared here [build] const char descriptor_table_protodef_msg_5fb_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = [build] ^ [build] /home/aobrien/dev/cpp/proto/build/src/msg_b/msg_b.pb.cc:70:3: error: cannot initialize an array element of type 'const ::google::protobuf::internal::DescriptorTable *const' with an rvalue of type 'const char (*)[92]' [build] &::descriptor_table_src_2fmsg_5fa_2fmsg_5fa_2eproto, [build] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [build] 3 errors generated.
问题根源
- 符号不匹配:
msg_b.proto的导入路径让Protobuf编译器生成了带src_2fmsg_5fa_2f前缀的内部符号,但msg_a编译时基于当前目录处理,生成的符号无此前缀,导致msg_b编译时找不到符号。 - 依赖未关联:
msg_b未链接msg_a库,无法获取msg_a生成的Protobuf内部符号。 - 头文件路径未传递:
msg_a未对外公开生成的头文件路径,msg_b无法正确引用msg_a的Protobuf头文件。
修复步骤
1. 调整msg_a的CMake配置
修改msg_a/CMakeLists.txt,公开生成的头文件路径:
protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS msg_a.proto) add_library(msg_a ${PROTO_HDRS} ${PROTO_SRCS}) # 公开Protobuf生成头文件的目录,让依赖库能找到 target_include_directories(msg_a PUBLIC ${CMAKE_BINARY_DIR}) target_link_libraries(msg_a protobuf::libprotobuf)
2. 修复msg_b的CMake配置
修改msg_b/CMakeLists.txt,添加对msg_a的依赖链接:
set(Protobuf_IMPORT_DIRS ${CMAKE_SOURCE_DIR}) protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS msg_b.proto) add_library(msg_b ${PROTO_HDRS} ${PROTO_SRCS}) # 链接msg_a库,确保能获取其生成的内部符号 target_link_libraries(msg_b PRIVATE msg_a protobuf::libprotobuf) # 无需单独设置CMAKE_BINARY_DIR,msg_a已通过target_include_directories传递路径
3. 可选:统一Protobuf生成路径(规范做法)
为避免生成的代码分散在build子目录,可统一输出到指定目录:
在顶层CMakeLists.txt中添加:
# 创建统一的Protobuf代码生成目录 set(PROTO_GENERATED_DIR ${CMAKE_BINARY_DIR}/proto) file(MAKE_DIRECTORY ${PROTO_GENERATED_DIR})
修改msg_a/CMakeLists.txt:
protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS OUT_DIR ${PROTO_GENERATED_DIR} msg_a.proto ) add_library(msg_a ${PROTO_HDRS} ${PROTO_SRCS}) target_include_directories(msg_a PUBLIC ${PROTO_GENERATED_DIR}) target_link_libraries(msg_a protobuf::libprotobuf)
修改msg_b/CMakeLists.txt:
set(Protobuf_IMPORT_DIRS ${CMAKE_SOURCE_DIR}) protobuf_generate_cpp(PROTO_HDRS PROTO_SRCS OUT_DIR ${PROTO_GENERATED_DIR} msg_b.proto ) add_library(msg_b ${PROTO_HDRS} ${PROTO_SRCS}) target_link_libraries(msg_b PRIVATE msg_a protobuf::libprotobuf) target_include_directories(msg_b PUBLIC ${PROTO_GENERATED_DIR})
这样所有生成的.pb.h和.pb.cc都会集中到build/proto目录,头文件引用更清晰。
内容的提问来源于stack exchange,提问作者Adam O'Brien
相关产品推荐
相关产品推荐

