CMake使⽤protobuf⽣成c++代码
⽬录标题
CMake编译protobuf⽣成c++代码
本⽂为个⼈使⽤cmake编译protobuf⽣成源码的经验总结,下⾯将介绍三种⽣成protobuf源码的cmake编写⽅式
1. protobuf_generate_cpp⽣成源码
cmake提供了FindProtobuf模块,可以通过find_package命令查找Protobuf进⾏使⽤,官⽹给的使⽤⽰例如下:
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
周公解梦梦见鬼
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS EXPORT_MACRO DLL_EXPORT foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS DESCRIPTORS PROTO_DESCS foo.proto)
protobuf_generate_python(PROTO_PY foo.proto)
add_executable(bar bar ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(bar ${Protobuf_LIBRARIES})
这⾥使⽤protobuf_generate_cpp命令将foo.proto⽂件⽣成源码,使⽤PROTO_SRC,PROTO_HARS变量分别指代⽣成的cpp和h⽂件并可⽤于连接到target和设置include
不过这种⽅法有两个缺点:
要求protobuf_generate_cpp命令和⽣成add_executable() 或 add_library() 的命令必须在同⼀个CMakeList中.
该⽅法(当前3.18)仍⽆法设置源码的⽣成路径,只能默认在相应的build-tree中⽣成
2.使⽤execute_process命令⽣成源码
为解决⽅法⼀中的缺点,可以使⽤cmake中的execute_process命令调⽤protoc程序来⾃定义⽣成源码的⽅法,⽰例如下:
SET(DST_DIR ${MESSAGE_DIR})
el()
file(MAKE_DIRECTORY ${MESSAGE_DIR})
港式按摩SET(DST_DIR ${MESSAGE_DIR})
endif()
#设置protoc的搜索路径
LIST(APPEND PROTO_FLAGS -I${CMAKE_SOURCE_DIR}/msg/message)
#获取需要编译的proto⽂件
file(GLOB_RECURSE MSG_PROTOS ${CMAKE_SOURCE_DIR}/msg/message/*.proto)
t(MESSAGE_SRC "")
t(MESSAGE_HDRS "")
foreach(msg ${MSG_PROTOS})
get_filename_component(FIL_WE ${msg} NAME_WE)
list(APPEND MESSAGE_SRC "${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb")
list(APPEND MESSAGE_HDRS "${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb.h")
吻戏最多的剧
# ⽣成源码
execute_process(
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTO_FLAGS} --cpp_out=${DST_DIR} ${msg}
)
endforeach()
t_source_files_properties(${MESSAGE_SRC} ${MESSAGE_HDRS} PROPERTIES GENERATED TRUE)
在⽰例中PROTOBUF_PROTOC_EXECUTABLE指代protobuf⽣成源码的可执⾏程序protoc,其使⽤格式为
protoc -I=$SRC_DIR --cpp_out=$DST_DIR$SRC_DIR/addressbook.proto
其中
-I=$SRC_DIR 为编译时搜索proto的根⽬录
健脾养胃的药–cpp_out=$DST_DIR为源码输出路径
$SRC_DIR/addressbook.proto 为需要编译的proto⽂件
在⽰例代码中锐利的反义词是什么
⾸先设置了源码输出路径DST_DIR并且当DST_DIR不存在时⽣成该⽬录;
之后通过*LIST(APPEND PROTO_FLAGS -I${CMAKE_SOURCE_DIR}/msg/message)*对protoc的搜
索路径进⾏设置;
最后execute_process命令遍历中的每个proto⽂件执⾏protoc命令,并将⽣成的源码分别追加到变
量MESSAGE_SRC和MESSAGE_HDRS中;
MESSAGE_SRC和MESSAGE_HDRS可以⽤于连接target和设置include_directories
这种⽅法仍然存在缺点:每次执⾏cmake后,都会重新⽣成proto源码,导致make时会因为源码变动(内容未变,只是重新⽣成)⽽重新编译程序3.使⽤add_custom_target与add_custom_command⽣成源码
为解决⽅法⼆中重新编译的问题,在该⽅法中引⼊add_custom_target命令⽣成⼀个⾃定义target,并令该target依赖于⽣成源码的
add_custom_command命令
SET(PROTO_META_BASE_DIR ${MESSAGE_DIR})
el()
file(MAKE_DIRECTORY ${MESSAGE_DIR})
SET(PROTO_META_BASE_DIR ${MESSAGE_DIR})
endif()
#设置protoc的搜索路径
辞章LIST(APPEND PROTO_FLAGS -I${CMAKE_SOURCE_DIR}/msg/message)
#获取需要编译的proto⽂件
file(GLOB_RECURSE MSG_PROTOS ${CMAKE_SOURCE_DIR}/msg/message/*.proto)
t(MESSAGE_SRC "")
t(MESSAGE_HDRS "")
foreach(msg ${MSG_PROTOS})
get_filename_component(FIL_WE ${msg} NAME_WE)
list(APPEND MESSAGE_SRC "${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb")
list(APPEND MESSAGE_HDRS "${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb.h")
# 使⽤⾃定义命令
add_custom_command(
OUTPUT "${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb"
"${PROJECT_BINARY_DIR}/message/${FIL_WE}.pb.h"
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS --cpp_out ${PROTO_META_BASE_DIR}
-I ${CMAKE_SOURCE_DIR}/msg/message
${msg}
DEPENDS ${msg}
COMMENT "Running C++ protocol buffer compiler on ${msg}"
VERBATIM
古诗硬笔书法
)
endforeach()
# 设置⽂件属性为 GENERATED
t_source_files_properties(${MESSAGE_SRC} ${MESSAGE_HDRS} PROPERTIES GENERATED TRUE)
# 添加⾃定义target
add_custom_target(generate_message ALL
DEPENDS ${MESSAGE_SRC} ${MESSAGE_HDRS}
COMMENT "generate message target"
VERBATIM
)
可以看到本⽅法的前⼀部分与⽅法⼆相似,不同点只在于使⽤了add_custom_command替换了execute_process命令,将
add_custom_command的OUTPUT与add_custom_target的DEPENDS绑定,即可依据绑定的target是否变动来决定源码⽣成命令是否执⾏(绑定⽅法:add_custom_command的OUTPUT为MESSAGE_SRC和MESSAGE_HDRS,同时MESSAGE_SRC和MESSAGE_HDRS为add_custom_target的DENPENDS)
其中值得主要的有两点:
设置⽣成的源码⽂件属性GENERATED为TRUE,否则cmake时会因找不到源码⽽报错
使⽤add_custom_target添加⽬标时要设置ALL关键字,否则target将不在默认编译列表中
这样就能实现proto⽣成源码配置定制化并避免不必要的重新编译了
4.总结老子西出函谷关