CMake从多目录带import的proto文件生成代码的正确方法
CMake多目录带互依赖Proto文件生成C++代码正确配置方案
问题根因
原生protobuf_generate_cpp函数处理绝对路径proto输入时的路径解析逻辑存在缺陷,无法正确保留输入proto相对于import根目录的层级结构,导致生成文件路径混乱,和proto中import声明生成的#include路径不匹配,同时会引发MSBuild项目识别生成文件路径错误的问题。
推荐配置方案(CMake 3.10+)
使用Protobuf模块内置的更灵活的protobuf_generate函数,显式控制路径参数,完美保留原始目录结构:
find_package(Protobuf REQUIRED) add_library(mylib STATIC "") # 定义proto源码根目录和生成文件输出根目录 set(PROTO_ROOT ${CMAKE_CURRENT_LIST_DIR}) set(PROTO_GEN_ROOT ${CMAKE_CURRENT_BINARY_DIR}) # 收集所有proto文件,使用相对于PROTO_ROOT的相对路径 file(GLOB_RECURSE PROTO_FILES RELATIVE ${PROTO_ROOT} ${PROTO_ROOT}/A/*.proto ${PROTO_ROOT}/B/*.proto ${PROTO_ROOT}/B/C/*.proto ) # 生成C++代码,自动保留目录结构 protobuf_generate( LANGUAGE cpp OUT_VAR PROTO_GENERATED_FILES PROTOS ${PROTO_FILES} PROTOC_OUT_DIR ${PROTO_GEN_ROOT} IMPORT_DIRS ${PROTO_ROOT} ) # 配置头文件搜索路径 target_include_directories(mylib PUBLIC ${Protobuf_INCLUDE_DIRS} ${PROTO_GEN_ROOT} ) # 添加生成文件到编译目标 target_sources(mylib PRIVATE ${PROTO_GENERATED_FILES} ) # 声明构建依赖,保证proto代码先于库编译生成 add_dependencies(mylib ${PROTO_GENERATED_FILES})
方案说明
- 所有proto文件以相对于源码根目录的相对路径传入,避免绝对路径导致的解析错误
- 显式指定
PROTOC_OUT_DIR为生成根目录,protoc会自动根据输入proto的相对路径,在输出根目录下创建相同层级的子目录存放生成文件,完全匹配proto中import声明生成的#include路径 - 所有生成文件的完整路径会自动传递给编译目标,MSBuild可正确识别文件位置,不会出现找不到文件的错误
兼容低版本CMake配置方案(3.10以下)
如果使用的CMake版本不支持protobuf_generate函数,可以手动调用protoc生成代码,完全控制生成逻辑:
find_package(Protobuf REQUIRED) add_library(mylib STATIC "") set(PROTO_ROOT ${CMAKE_CURRENT_LIST_DIR}) set(PROTO_GEN_ROOT ${CMAKE_CURRENT_BINARY_DIR}) # 收集所有proto文件,使用相对于PROTO_ROOT的相对路径 file(GLOB_RECURSE PROTO_FILES RELATIVE ${PROTO_ROOT} ${PROTO_ROOT}/A/*.proto ${PROTO_ROOT}/B/*.proto ${PROTO_ROOT}/B/C/*.proto ) set(PROTO_GENERATED_FILES "") foreach(PROTO_FILE ${PROTO_FILES}) get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY) get_filename_component(PROTO_NAME_WE ${PROTO_FILE} NAME_WE) # 计算生成文件的完整路径 set(GEN_SRC "${PROTO_GEN_ROOT}/${PROTO_DIR}/${PROTO_NAME_WE}.pb.cc") set(GEN_HDR "${PROTO_GEN_ROOT}/${PROTO_DIR}/${PROTO_NAME_WE}.pb.h") list(APPEND PROTO_GENERATED_FILES ${GEN_SRC} ${GEN_HDR}) # 添加自定义生成命令 add_custom_command( OUTPUT ${GEN_SRC} ${GEN_HDR} COMMAND ${Protobuf_PROTOC_EXECUTABLE} ARGS --cpp_out=${PROTO_GEN_ROOT} -I ${PROTO_ROOT} ${PROTO_ROOT}/${PROTO_FILE} DEPENDS ${PROTO_ROOT}/${PROTO_FILE} COMMENT "Generating C++ code for ${PROTO_FILE}" VERBATIM ) endforeach() target_include_directories(mylib PUBLIC ${Protobuf_INCLUDE_DIRS} ${PROTO_GEN_ROOT} ) target_sources(mylib PRIVATE ${PROTO_GENERATED_FILES} )
这个方案兼容性更强,生成的目录结构和原始proto的目录结构完全一致,可解决所有提到的问题。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

