前言
最近在移植apollo7.0.0代码时,遇到跨文件夹import依赖Proto文件,采用以前小项目的方式无法成功生成.cc和.h文件,耗费2-3天时间解决,故记录。
文件夹与子文件夹内部依赖(小项目)
- 项目目录如下:
.
├── CMakeLists.txt
├── PathBackup.proto
├── PathCurrent.proto
├── PathNode.proto
├── PathRouting.proto
├── adas.proto
├── bag_manager.proto
├── behaviors_decision.proto
├── caliLidar.proto
├── chassis.proto
├── control_command.proto
├── hadmap_traffic_light.proto
├── header.proto
├── lane_mark.proto
├── v2x
│ ├── AccelerationSet4Way_PB.proto
│ ├── BSM_PB.proto
│ ├── BasicSafetyMessage_PB.proto
│ ├── BrakeSystemStatus_PB.proto
│ ├── DDateTime_PB.proto
│ ├── EventSource_PB.proto
v2x/ 依赖上层目录的header.proto
文件,比如:
import路径仅包含文件名即可
syntax = "proto2";
package common;
import "header.proto";
import "BasicSafetyMessage_PB.proto";
message BSM_PB {
required BasicSafetyMessage_PB bsmFrame =1;
optional common.Header header = 2;
}
CMakeLists如下:
- CPP
cmake_minimum_required(VERSION 3.5)
project(common_proto)
find_package(Protobuf REQUIRED)
set(proto_dir ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB proto_files "${proto_dir}/*.proto")
file(GLOB proto_files_2 "${proto_dir}/v2x/*.proto")
list(APPEND proto_files ${proto_files_2})
set(proto_gen_cpp_dir ${CMAKE_CURRENT_SOURCE_DIR})
# Create lists of files to be generated
set(proto_gen_cpp_files "")
foreach(proto_file ${proto_files})
get_filename_component(proto_name ${proto_file} NAME_WE)
list(APPEND proto_gen_cpp_files
${proto_gen_cpp_files}/${proto_name}.pb.cc
)
endforeach(proto_file ${proto_files})
# Run protoc and generate language-specific headers.
add_custom_command(
OUTPUT ${proto_gen_cpp_files}
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
--proto_path=${proto_dir_2}
--proto_path=${proto_dir}
--cpp_out=${proto_gen_cpp_dir} ${proto_files}
DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
set_source_files_properties(${proto_gen_cpp_files} PROPERTIES GENERATED TRUE)
include_directories(
#${PROTOBUF_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/proto
)
add_library(${PROJECT_NAME} ${proto_gen_cpp_files})
target_link_libraries(${PROJECT_NAME} ${PROTOBUF_LIBRARY})
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
- PYTHON
cmake_minimum_required(VERSION 3.5)
project(common)
find_package(Protobuf REQUIRED)
set(proto_dir ${PROJECT_SOURCE_DIR}/proto)
file(GLOB proto_files "${proto_dir}/*.proto")
file(GLOB proto_files_2 "${proto_dir}/v2x/*.proto")
list(APPEND proto_files ${proto_files_2})
message(STATUS "Proto Source Dir: ${proto_dir}")
message(STATUS "Proto Source Files: ${proto_files}")
set(proto_gen_py_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
file(MAKE_DIRECTORY ${proto_gen_py_dir})
file(WRITE ${proto_gen_py_dir}/__init__.py)
set(proto_gen_py_files "")
foreach(proto_file ${proto_files})
get_filename_component(proto_name ${proto_file} NAME_WE)
list(APPEND proto_gen_py_files ${proto_gen_py_dir}/${proto_name}_pb2.py)
endforeach(proto_file ${proto_files})
add_custom_command(
OUTPUT ${proto_gen_py_files}
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${proto_dir}/v2x --proto_path=${proto_dir} --python_out=${proto_gen_py_dir} ${proto_files}
DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(runa ALL DEPENDS ${proto_gen_py_files})
set_source_files_properties(${proto_gen_py_files} PROPERTIES GENERATED TRUE)
install(DIRECTORY ${proto_gen_py_dir}/
DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
FILES_MATCHING PATTERN "*.py"
)
install(DIRECTORY include
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}/common
FILES_MATCHING PATTERN "*.h"
)
install(DIRECTORY proto
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}/common
FILES_MATCHING PATTERN "*.h"
)
文件夹与文件夹之间依赖(大项目)
- 项目目录如下:
.
├── CMakeLists.txt
├── apollo_planning
│ └── planning_base
│ └── proto
│ └── auto_tuning_raw_feature.proto
└── common_msgs
├── BUILD
├── CMakeLists.txt
├── README.md
└── basic_msgs
├── BUILD
├── direction.proto
├── drive_event.proto
├── drive_state.proto
├── error_code.proto
├── geometry.proto
├── header.proto
├── pnc_point.proto
├── vehicle_id.proto
└── vehicle_signal.proto
其中auto_tuning_raw_feature.proto
文件如下:
syntax = "proto2";
package apollo.planning.autotuning;
import "common_msgs/basic_msgs/pnc_point.proto";
message PathPointRawFeature {
optional apollo.common.PathPoint cartesian_coord = 1;
optional apollo.common.FrenetFramePoint frenet_coord = 2;
}
message SpeedPointRawFeature {
message ObjectDecisionFeature {
// obstacle id
optional int32 id = 1;
// relative to eog, s_obs - s_host [m]
optional double relative_s = 2;
// relative to ego, l_obs - l_host [m]
optional double relative_l = 3;
// relative to ego, v_obs - v_host [m/s]
optional double relative_v = 4;
// speed [m / s]
optional double speed = 5;
}
optional double s = 1; // [m]
optional double t = 2; // [s]
optional double v = 3; // [m/s]
optional double a = 4; // [m/s^2]
optional double j = 5; // [m/s^3]
optional double speed_limit = 6; // speed limit with curvature adj [m/s]
repeated ObjectDecisionFeature follow = 10;
repeated ObjectDecisionFeature overtake = 11;
repeated ObjectDecisionFeature virtual_decision = 13;
repeated ObjectDecisionFeature stop = 14;
repeated ObjectDecisionFeature collision = 15;
repeated ObjectDecisionFeature nudge = 12;
repeated ObjectDecisionFeature sidepass_front = 16;
repeated ObjectDecisionFeature sidepass_rear = 17;
repeated ObjectDecisionFeature keep_clear = 18;
}
// caputuring the obstacle raw distance information from surrounding environment
// based on ST graph
message ObstacleSTRawData {
message STPointPair {
optional double s_lower = 1;
optional double s_upper = 2;
optional double t = 3;
optional double l = 4 [default = 10.0]; // filled when nudging
}
message ObstacleSTData {
optional int32 id = 1;
optional double speed = 2;
optional bool is_virtual = 3;
optional double probability = 4;
repeated STPointPair polygon = 8;
repeated STPointPair distribution = 9;
}
repeated ObstacleSTData obstacle_st_data = 1;
repeated ObstacleSTData obstacle_st_nudge = 2;
repeated ObstacleSTData obstacle_st_sidepass = 3;
}
message TrajectoryPointRawFeature {
optional PathPointRawFeature path_feature = 1;
optional SpeedPointRawFeature speed_feature = 2;
}
message TrajectoryRawFeature {
repeated TrajectoryPointRawFeature point_feature = 1;
optional ObstacleSTRawData st_raw_data = 2;
}
如上auto_tuning_raw_feature.proto引用common_msgs/basic_msgs/pnc_point.proto,但这两个文件不在一个目录下,无法直接使用第一种方式。
如果粗暴一点,就是将所有proto代码放到一个目录下,缺点是import路径全部得改,对于大型项目不太适用,大型项目指apollo,mediapipe等。(项目并不是采用cmake来构建项目,而是使用google自家研发的bazel)
并且,期望生成的.cc和.h文件在原始的proto目录下
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(MODULES)
set(MODULES_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}")
message("MODULES_INCLUDE_DIRS: << " ${MODULES_INCLUDE_DIRS})
# set(MODULES_LINKER_LIBS "")
# =============================
# generate proto .cc .h
# =============================
find_package(Protobuf REQUIRED)
# 需要编译的.proto文件:
file(GLOB protobuf_files
common_msgs/basic_msgs/*.proto
common_msgs/chassis_msgs/*.proto
common_msgs/config_msgs/*.proto
common_msgs/dreamview_msgs/*.proto
common_msgs/external_command_msgs/*.proto
common_msgs/localization_msgs/*.proto
common_msgs/map_msgs/*.proto
common_msgs/monitor_msgs/*.proto
common_msgs/planning_msgs/*.proto
common_msgs/perception_msgs/*.proto
common_msgs/prediction_msgs/*.proto
common_msgs/routing_msgs/*.proto
common_msgs/storytelling_msgs/*.proto
apollo_planning/planning_base/proto/*.proto
apollo_planning/planning_base/proto/math/*.proto
apollo_planning/planning_interface_base/traffic_rules_base/proto/*.proto
common/util/testdata/*.proto
common/vehicle_state/proto/*.proto
)
# 定义相关的目录地址,PROTO_META_BASE_DIR为编译之后生成文件的目录。
# PROTO_FLAGS很重要,指定编译.proto文件时的总的寻找路径,
# .proto中的import命令根据根据这个地址去连接其他的.proto文件:
SET(PROTO_META_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
LIST(APPEND PROTO_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR})
# 通过FOREACH去循环之前的.proto文件,依次编译每个文件,
# 然后将生成的.pb.cc和.pb.h移动回原始的目录
FOREACH(FIL ${protobuf_files})
GET_FILENAME_COMPONENT(FIL_WE ${FIL} NAME_WE)
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" FILE_NAME ${FIL})
string(REGEX REPLACE "(.+)\\${FILE_NAME}.*" "\\1" FILE_PATH ${FIL})
string(REGEX MATCH "(/common_msgs.*|/apollo_planning/planning_base/proto.*|/apollo_planning/planning_interface_base/traffic_rules_base/proto.*|/common/util/testdata.*|/common/vehicle_state/proto.*)" OUT_PATH ${FILE_PATH})
set(PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.cc")
set(PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.h")
EXECUTE_PROCESS(
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTO_FLAGS} --cpp_out=${PROTO_META_BASE_DIR} ${FIL}
)
message("Copying " ${PROTO_SRCS} " to " ${FILE_PATH})
file(COPY ${PROTO_SRCS} DESTINATION ${FILE_PATH})
file(COPY ${PROTO_HDRS} DESTINATION ${FILE_PATH})
ENDFOREACH()
# =============================
# add modules
# =============================
include_directories(
${MODULES_INCLUDE_DIRS}
)
参考链接
Protobuf在Cmake中的正确使用 - Oldpan的个人博客
blog.csdn.net