CMake 下使用 protobuf

protobuf是google开发的一个序列化和反序列化的库,通过.proto文件定义文件格式,序列化后的数据是binary的,可以在多语言上使用.一般情况下,若客户端和服务端都是自己做的,用protobuf作为通讯协议,无疑是一个不错的选择.

个人一般喜欢用CMake来管理c++工程.那么,cmake下使用protobuf对个人而言是个很重要的事情.

我准备了一个简单的工程,如此处所示.

cmake有官方的modules,文件是FindProtobuf.cmake,里面有宏PROTOBUF_GENERATE_CPP.用法据介绍如下:

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS foo.proto)
ADD_EXECUTABLE(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
TARGET_LINK_LIBRARIES(bar ${PROTOBUF_LIBRARIES})

很厉害是吧..

现在我有一组文件如下:

├── CMakeLists.txt
├── README.md
├── meta
│   └── proto
│   ├── CMakeLists.txt
│   └── common
│   ├── bar
│   │   ├── CMakeLists.txt
│   │   └── bar.proto
│   └── foo
│   ├── CMakeLists.txt
│   └── foo.proto
└── src
├── CMakeLists.txt
├── c_proto.cc
└── c_proto.hh

其中foo.proto文件如下:

message foo_msg
{
optional string name = 1;
}

bar.proto文件如下:

import "common/foo/foo.proto";

message bar_msg
{
optional foo_msg foo = 1;
optional string name = 2;
}

如上是对foo_msg和bar_msg的定义,bar引用了foo.开始我是使用的PROTOBUF_GENERATE_CPP那个宏,然后在有import的时候,妥妥的跪了,google家的也表示不解.

然后直接骚扰下cmake的mail list.然后第二天,一个好心的中国人告诉我,他们都是自定义protoc的命令的.

经过一些挣扎修改,最后决定还是手工拼命令.

FIND_PACKAGE(Protobuf REQUIRED)

find宏可以获得PROTOBUF_LIBRARIES,所有用到proto的需要依赖这组lib.

然后在proto的逻辑root目录下设置好参数,把它加入proto flags list中

SET(PROTO_META_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
LIST(APPEND PROTO_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR})

然后代码如下:

FILE(GLOB BAR_PROTOS "*.proto")

FOREACH(FIL ${BAR_PROTOS})
GET_FILENAME_COMPONENT(ABS_FIL ${FIL} ABSOLUTE)
GET_FILENAME_COMPONENT(FIL_WE ${FIL} NAME_WE)

LIST(APPEND BAR_SRCS "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc")
LIST(APPEND BAR_HDRS "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h")

EXECUTE_PROCESS(
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTO_FLAGS} --cpp_out=${PROTO_META_BASE_DIR} ${FIL}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
ENDFOREACH()

ADD_LIBRARY(bar ${BAR_SRCS})
TARGET_LINK_LIBRARIES(bar foo)