You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 21:36:00