子目录中带有 protobuf 文件的 CMake

CMake with protobuf file in subdirectory

我看过很多类似的问题和答案,但直到现在它似乎还不是很明显。我对 CMake 很陌生,直到现在一切都很简单,除了与协议缓冲区的集成。

我有一个包含子目录的项目,其中每个子目录都有自己的 CMakeLists.txt

其中一个子目录包含一个 .proto 文件。如果执行 PROTOBUF_GENERATE_CPP 宏,它会生成源文件和头文件。该宏是从包含 .proto 文件的子目录中的 CMakeLists.txt 调用的。

不过似乎没有调用 make 文件,因为没有向目标添加源。我无法将源添加到目标,因为文件不存在,它们在生成后存在,所以这会导致 CMake 运行时出错。

将文件属性设置为生成似乎也无济于事。一般来说,在构建过程开始之前,宏应该已经 运行 生成了源文件。

如何做到这一点,有任何工作示例吗?

示例:

./src/externals/protodef(来自其他存储库,仅包含 .proto 文件)
./src/generated (protoc 生成的 c 文件和头文件)

CMakeLists-1(项目根目录)

cmake_minimum_required (VERSION 2.6)
PROJECT (prototest)

ADD_SUBDIRECTORY("${PROJECT_SOURCE_DIR}/src/externals/protodef")
ADD_SUBDIRECTORY("${PROJECT_SOURCE_DIR}/src")

SET_SOURCE_FILES_PROPERTIES(${PROTO_SOURCES} ${PROTO_HEADERS} PROPERTIES GENERATED TRUE)

ADD_EXECUTABLE(prototest ${PROTO_SOURCES} ${SOURCE} )
TARGET_LINK_LIBRARIES(prototest ${EXTERNAL_LIBS} )

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

CMakeLists-2 (src)

SET(SOURCE ${SOURCE}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp

  PARENT_SCOPE
)

CMakeLists-3 (src/externals/protodef)

SET(PROTOBUF_PATH "D:/protobuf-3.0.0/" )
SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "${PROTOBUF_PATH}")

# Changing PROTO_SRCS and PROTO_HDRS does not work for setting the location 
# of the generated files.
# Those variable are ignored by CMake for compiling the proto files.
# Using a dedicated CMakeLists.txt and settng CURRENT_BINARY dir is a 
# workaround to get them where we want.
SET(GENERATED_DIR ${PROJECT_SOURCE_DIR}/src/generated )
SET(CMAKE_CURRENT_BINARY_DIR ${GENERATED_DIR} )


INCLUDE(FindProtobuf)
FIND_PACKAGE(Protobuf REQUIRED)

PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS${CMAKE_CURRENT_SOURCE_DIR}/test1.proto)

SET( EXTERNAL_LIBS ${PROTOBUF_PATH}/lib/libprotobuf.a PARENT_SCOPE)

# Propagate sources to the parant project
SET(PROTO_SOURCES ${PROTO_SRCS}
PARENT_SCOPE
)

SET(PROTO_HEADERS ${PROTO_HDRS}
PARENT_SCOPE
)

首先生成 protobuf 文件,然后将它们添加到 CMake 目标。

CMakeLists(源代码):

# Generate h/cpp proto files (./src/externals/protodef) into ./src/generated folder
PROTOBUF_GENERATE_CPP(...)

# Process subdir
ADD_SUBDIRECTORY(generated)

好像PROTOBUF_GENERATE_CPP只能在同一个子目录下使用。一个可能的解决方法是直接调用 protoc :

 FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/externals/protodef PROTOMODEL_PATH)
 FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/generated PROTOBINDING_PATH)

 FILE(GLOB DATAMODEL_PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/externals/protodef/*.proto")

 FOREACH(proto ${DATAMODEL_PROTOS})
    FILE(TO_NATIVE_PATH ${proto} proto_native)
    EXECUTE_PROCESS(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${PROTOMODEL_PATH} --cpp_out=${PROTOBINDING_PATH} ${proto_native}
    RESULT_VARIABLE rv)
    # Optional, but that can show the user if something have gone wrong with the proto generation 
    IF(${rv})
         MESSAGE("Generation of data model returned ${rv} for proto ${proto_native}")
    ENDIF()
 ENDFOREACH(proto)

CMakeLists (src/generated) :

## List generated sources files
FILE(GLOB HDRS "*.h")
FILE(GLOB SRCS "*.cc")

ADD_LIBRARY(protoBinding ${HDRS} ${SRCS})
# ${PROTOBUF_LIBRARIES} should be defined by FIND_PACKAGE(Protobuf REQUIRED)
TARGET_LINK_LIBRARIES(protoBinding ${PROTOBUF_LIBRARIES})

这样 CMake 将首先生成 header/source 个文件,然后才将生成的文件添加到 CMake 目标。

然后您可以使用 protoBinding 目标将生成的文件 link 发送到其他目标(例如在 src 的 CMakeLists.txt 末尾):

ADD_LIBRARY(myModel ${myFiles})
TARGET_LINK_LIBRARIES(myModel protoBinding)