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)
10 Comments
DINGFENG · September 2, 2020 at 21:46
我也遇到了这个问题,折磨了一整天!设置PROTOBUF_IMPORT_DIRS并不是正确的做法!最新版的cmake仍然没有提供一个直接设置 -I 的参数。
Yu · September 5, 2020 at 13:39
抱歉很久才看到您的回复。
目前看来文中的可能是目前最后妥协的结果了。不知道这个能不能解决你的问题。
ZHAO XIAOWEI · October 29, 2018 at 07:34
文章很赞啊
yu · October 29, 2018 at 23:46
@ZHAO XIAOWEI 汗,多谢。不过这篇有点久远,我怀疑内容已经过时了。
Neil · April 30, 2016 at 12:27
感谢分享!好文章!
yu · April 30, 2016 at 14:10
@Neil 多谢
amoylan · January 27, 2016 at 15:55
我也试了好久,最终发现不能百分百满足你的要求。两个文件不需要在一起generate,但是bar.proto必须import “foo.proto”才行。 在proto目录中的Cmakelilsts.txt中set(PROTO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}),在bar目录下set(PROTOBUF_IMPORT_DIRS ${PROTO_ROOT_DIR}/common/foo)即可 公司内网无法上传代码。。
yu · January 28, 2016 at 17:33
@amoylan 多谢. 尝试很久也无法做得更好了.
amoylan · January 26, 2016 at 17:50
博主好,首先来表达一下我对你的敬仰之情,我真的太喜欢这个博客了。
我在工作中遇到了和你一样的问题,一开始我从你这里copy了上述方法,并且成功运行了,于是我叫来架构师大哥,他分分钟找到了更好的办法,删除了我的改动。。。
这里的关键就是那个-I参数,cmake中这个参数名为PROTOBUF_IMPORT_DIRS,可以在源码中找到。因此,在执行protobuf_generate_cpp之前设置一下这个参数的值就可以了
yu · January 27, 2016 at 01:28
@amoylan 你好, 多谢你的提示.
我当时也注意到了这个参数. 当时我在文中提到的这个 stackoverflow 的问题里面 也提到了这个问题. 当时我也利用它做了一些操作, 最后能做成的是如这个链接所示的样子. 其中我在 proto 的根目录下配置是这样的. foo.proto 和 bar.proto 两个文件因为有依赖关系, 因此我PROTOBUF_GENERATE_CPP 似乎 必须要一起 generate 才不会报错. 但是若我一起 generate, 那么后果是 foo.pb.cc 和 bar.pb.cc 都跑到了一个文件夹下, 那么我 bar.proto 文件中的引用就大有问题了. bar.proto 的
必须改成
这样才能生效.
这并不是我想要的效果.
如果我设置的 proto 模型如下:
那么就会出错. 两个 foo.proto 造成了文件的冲突, 而且 bar.proto 也很难决定是引用哪个 proto 文件.
我知道同样的文件名并不是个好主意, 但个人还是挺希望兼容这种问题的.
若可以, 不知可否分享下你的方法? 拜谢.