ROS2_Foxy学习7——构建
官方文档总是很丰富,关于构建工具,参考 A universal build tool、 Using colcon to build packages、 ament_cmake user documentation。
1 前言
ROS生态中,一个软件是由多个分离的package组成的,运行软件,首先需要构建(build)这些package。
手动构建一组package的方法就是逐个构建,每个package都有特定的构建系统,需要根据依赖情况设置环境,然后build。
如果package及其依赖较多的话,其效率是难以接受的,这就需要一个自动化的构建工具,只需要调用一次即可构建一组package。
ROS1提供了多个自动构建工具,catkin_make, catkin_make_isolated, 和 catkin_tools。ROS2提供了一个自动构建工具,ament_tools。
2 构建工具与构建系统
2.1 构建工具
构建工具在一组package上运行,它确定依赖关系图,并以拓扑顺序为每个包调用特定的构建系统。构建工具只需要知道每个包特定的构建系统是如何设置环境以构建和使用包的即可。
构建工具例如catkin_make,catkin_make_isolated,catkin_tools,ament_tools,colcon。
2.2 构建系统
构建系统在单个package上运行。
构建系统例如Make,CMake,Python setuptools,ament等,ROS的catkin和ament_cmake基于CMake的。
(一般使用包括以下步骤:cmake, make, make install)
3 构建工具 colcon
3.1 colcon背景
colcon是ROS构建工具catkin_make、catkin_make_isolated、catkin_tools和ament_tools的迭代。colcon目标是做一个通用构建工具(universal build tool),能够构建ROS1和ROS2的包,同时也能够构建一些非ROS包。ROS中,catkin_make、catkin_make_isolated、catkin_tools、ament_tools将逐步被colcon取代。
3.2 colcon安装
$ sudo apt install python3-colcon-common-extensions
# 或者使用Python下载,如果无法定位软件包
# pip3 install -U colcon-common-extensions
3.3 colcon使用
1、创建工作空间
$ mkdir -p ~/ros2_example_ws/src
$ cd ~/ros2_example_ws
注:-p 的意义。
2、下载示例代码到src文件夹,并检查兼容性
$ git clone https://github.com/ros2/examples src/examples
# check
$ cd ~/ros2_example_ws/src/examples/
$ git checkout $ROS_DISTRO
$ cd ~/ros2_example_ws
完成后,工作空间的文件组织结构如下
.
└── src
└── examples
├── CONTRIBUTING.md
├── LICENSE
├── rclcpp
├── rclpy
└── README.md
3、在工作空间的根目录编译示例代码
$ cd ~/ros2_example_ws
$ colcon build --symlink-install
# --symlink-install* 的意义么看懂?不加,也能编译成功。
完成后,工作空间组织结构如下
.
├── build
├── install
├── log
└── src
└── examples
├── CONTRIBUTING.md
├── LICENSE
├── rclcpp
├── rclpy
└── README.md
4、测试示例代码
$ colcon test
5、配置环境变量,然后运行节点
$ . install/setup.bash
$ ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function
# 打开另一个终端
$ . install/setup.bash
$ ros2 run examples_rclcpp_minimal_publisher publisher_member_function
3.4 tips
1、COLCON_IGNORE:在不需要编译的包中添加文件COLCON_IGNORE,colcon将忽略这一功能包
2、colcon支持多种编译类型,推荐ament_cmake 和 ament_python,同时也支持纯cmake包。(在使用ros2 pkg create命令创建功能包时,可以使用–build-type参数来指定该功能包的编译类型,然后按模板生成功能包)
$ touch COLCON_IGNORE
3.5 colcon_cd命令
colcon_cd命令的目的是从当前目录,换成某个已有的package目录。
1、colcon_cd安装
参考官方,通过命令行安装二进制文件
$ sudo sh -c 'echo "deb [arch=amd64,arm64] http://repo.ros2.org/ubuntu/main `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-latest.list'
$ curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
$ sudo apt update
$ sudo apt install python3-colcon-common-extensions
2、colcon_cd使用
# 获取安装文件
$ echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
# 提前指定包的位置
$ echo "export _colcon_cd_root=~/ros2_install" >> ~/.bashrc
# 目录变换到 ~/ros2_install 工作空间下的 some_ros_package 包
$ colcon_cd some_ros_package
4 构建系统 ament
ament构建系统包括两个基本的子类,ament_python和ament_cmake,这里只讨论ament_cmake。
使用ament_cmake编译系统的功能包会对应生成两个文件package.xml和CMakeLists.txt。
4.1 编写 package.xml
package.xml描述了功能包的发行信息、依赖信息等,内容如下。
在自动创建后,需要手动修改功能包描述、功能包版本、功能包许可证信息等发行信息。以“ _depend”结尾的标签用来描述功能包依赖,在增加新的依赖时,需要添加到其中。
<!-- 功能包基础内容,不用修改 -->
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!-- 下面是功能包的信息 -->
<package format="3">
<!-- 包名,新建的时候就定了,不改 -->
<name>cpp_srvcli</name>
<!-- 关于发行信息 -->
<version>0.0.0</version>
<description>这里写功能描述</description>
<maintainer email="这里写邮箱地址">Your Name</maintainer>
<license>这里写许可证</license>
<!-- 编译系统的依赖 -->
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- 程序所需的依赖,根据需要修改添加 -->
<depend>rclcpp</depend>
<!-- 编译系统,新建功能包的时候可以指定 -->
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
4.2 编写 CMakeLists.txt
1、基本框架
cmake_minimum_required(VERSION 3.5) //cmake版本要求
project(my_project) //功能包名,与同一功能包下package.xml中名字一样
ament_package() //配置project,只调用一次,且在最后
ament_package()的作用有这么几个:
第一,安装package.xml文件,ament_package()提供了查找和解析package.xml文件的API;
第二,按ament索引,注册功能包;
第三,安装cmake配置文件(也就是生成.cmake文件,名字类似于FindXXX.cmake或者xxxConfig.cmake)以便于其他包要依赖这个包时,能被cmake找到。
2、编译目标
编译目标包括 libraries 和 executables。
对于编译库,cmake语法如下。
# A 创建库,并指定编译时的源文件
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
# STATIC : 静态库
# SHARED : 共享库
# MODULE : 在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待
# B 编译这个库时要包含的头文件,必须先使用add_library创建
target_include_directories(<name> <INTERFACE|PUBLIC|PRIVATE>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
# CMAKE_CURRENT_SOURCE_DIR :当前处理的CMakeLists.txt所在的目录
# BUILD_INTERFACE : 构建时的接口
# INSTALL_INTERFACE :安装时的接口
# EXAMPLE
add_library(action_server SHARED src/fibonacci_action_server.cpp)
target_include_directories(action_server PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
对于编译可执行文件,cmake语法如下。
# A 创建可执行文件,并指定编译时的源文件
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
# WIN32 MACOSX_BUNDLE 分别是Windows和Mac系统的构建选项
# EXCLUDE_FROM_ALL选项增加,表示不构建此可执行文件
# EXAMPLE
add_executable(server src/add_three_ints_server.cpp)
3、编译依赖
编译依赖有两种方式,ament_target_dependencies和target_link_libraries。
首先推荐前者,自动包含头文件、库及其依赖项,使用覆盖工作区时,将确保正确排列所有依赖项的包含目录。
当前者无法找到库时,使用后者。例如自定义的库文件,仍需要使用target_link_libraries的方式链接。语法类似,但需要包含必要的依赖和使用include_directories()包含必要的头文件。
同样,目标要先创建,才能添加依赖。
# ament_target_dependencies
find_package(dependence REQUIRED)
ament_target_dependencies(target Eigen3)
# EXAMPLE
find_package(rclcpp REQUIRED)
ament_target_dependencies(action_server
"rclcpp")
4、导出库
(这个,后面用到的时候再补充…)
ament_export_targets(export_my_library HAS_LIBRARY_TARGET)
ament_export_dependencies(some_dependency)
install(
DIRECTORY include/
DESTINATION include
)
install(
TARGETS my_library
EXPORT export_my_library
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
5、install命令
# A cmake的语法如下
install(TARGETS targets... [EXPORT <export-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
# EXAMPLE
install(TARGETS
action_server
action_client
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
# ARCHIVE 安装静态库到 ${CMAKE_INSTALL_LIBDIR}/lib
# LIBRARY 安装动态库到 ${CMAKE_INSTALL_LIBDIR}/lib
# RUNTIME 安装可执行二进制文件到 ${CMAKE_INSTALL_BINDIR}/bin
附:除官网外的其他参考
1、https://cmake.org/cmake/help/v3.19/command/target_include_directories.html
2、ROS学习日记2