CMakeLists.txt文件的深入理解
在 ROS2 开发过程中,CMakeLists.txt 文件至关重要。ROS2 功能包通常包含节点、库、头文件及其他资源,这些内容的构建和组织需要借助CMake实现。具体来说,ROS2 使用 CMakeLists.txt 完成以下任务:
- 查找和管理ROS 2的依赖项,如
rclcpp、std_msgs等。 - 编译节点的可执行文件和动态链接库。
- 配置节点运行时所需的路径和环境。
- 定义安装路径,以便节点和其他资源能够被 ROS2 系统识别和使用。 通过规范的 CMakeLists.txt 配置,开发者能够确保功能包的构建过程清晰且易于维护,使 ROS2 项目更加稳定、可扩展和易于移植。
ROS 2中的路径
ROS2 中的路径问题是初学者经常感到困惑的地方。下面从概念到具体实践,帮助读者更好地理解 ROS2 和CMakeLists.txt 中的路径问题。
ROS2 涉及的路径主要分为以下几种:
-
源码路径(Source Path)
- 功能包源文件、头文件所在的位置,通常位于工作空间中,如:
~/ros2_ws/src/my_package/其中,my_package表示创建的功能包的名称。
- 功能包源文件、头文件所在的位置,通常位于工作空间中,如:
-
编译路径(Build Path)
- 功能包编译时生成的临时文件,如编译中间文件
.o等,通常在:~/ros2_ws/build/my_package/
- 功能包编译时生成的临时文件,如编译中间文件
-
安装路径(Install Path)
-
功能包编译完成后生成的可执行文件、动态库、头文件安装位置,位于:
~/ros2_ws/install/my_package/ -
具体如:
- 可执行文件:
~/ros2_ws/install/my_package/lib/my_package/ - 头文件:
~/ros2_ws/install/my_package/include/
- 可执行文件:
-
-
系统路径与环境变量(如ROS_PACKAGE_PATH)
- ROS2 运行时通过环境变量定位功能包路径,加载安装目录的
setup.bash使得 ROS2 能够找到功能包:
source ~/ros2_ws/install/setup.bash - ROS2 运行时通过环境变量定位功能包路径,加载安装目录的
CMakeLists.txt中常见路径的用法
在 CMakeLists.txt 文件中,路径设置一般使用特殊变量和宏,常用的如下:
1. ${CMAKE_CURRENT_SOURCE_DIR}
表示当前 CMakeLists.txt文 件所在目录(源码目录)。
示例:
target_include_directories(talker PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> )
表示编译阶段头文件路径在源码目录下的include文件夹中。
2. ${CMAKE_INSTALL_PREFIX}
表示安装时的根路径,一般在工作空间的install目录中(如~/ros2_ws/install)。
示例:
install(TARGETS talker DESTINATION lib/${PROJECT_NAME} )
此命令会安装节点到:
~/ros2_ws/install/my_package/lib/my_package/talker
3. $<INSTALL_INTERFACE:include> 和 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
这是CMake的生成器表达式(generator expressions):
-
BUILD_INTERFACE 表示编译时路径(通常为源码位置)
-
INSTALL_INTERFACE 表示安装后路径(通常位于install目录下)
例如:
target_include_directories(my_node PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # 编译时头文件路径 $<INSTALL_INTERFACE:include> # 安装后头文件路径 )
-
编译阶段,CMake使用
~/ros2_ws/src/my_package/include -
安装阶段,CMake将头文件安装到
~/ros2_ws/install/my_package/include
常用的CMake路径变量
| 变量名称 | 作用与含义 | 示例路径 |
|---|---|---|
${CMAKE_CURRENT_SOURCE_DIR} | 当前正在处理的CMakeLists.txt所在的目录 | ~/ros2_ws/src/my_package |
${CMAKE_CURRENT_BINARY_DIR} | 当前构建目录,编译时生成的临时文件路径 | ~/ros2_ws/build/my_package |
${CMAKE_SOURCE_DIR} | 顶级源码目录,通常为整个工作空间的根目录 | ~/ros2_ws/src/my_package |
${CMAKE_BINARY_DIR} | 顶级构建目录,整个构建过程的临时文件路径 | ~/ros2_ws/build/my_package |
${CMAKE_INSTALL_PREFIX} | 安装时使用的根路径,通常对应install目录 | ~/ros2_ws/install |
${PROJECT_SOURCE_DIR} | 当前项目的源码根目录,与CMAKE_CURRENT_SOURCE_DIR类似 | ~/ros2_ws/src/my_package |
${PROJECT_BINARY_DIR} | 当前项目的编译根目录,与CMAKE_CURRENT_BINARY_DIR类似 | ~/ros2_ws/build/my_package |
${PROJECT_NAME} | 当前项目名称,与project(<project_name>)命令定义的名称一致 | my_package |
这些变量允许你在构建过程中灵活地引用路径,确保项目的可移植性和稳定性。下面将通过一个标准的 CMakeLists.txt 学习这些变量。
ROS2 中 CMakeLists.txt 的标准结构
假设我们有一个功能包 my_package,如下结构:
my_package/
├── include/
│ └── my_package/
│ └── my_header.hpp
├── src/
│ └── my_node.cpp
├── CMakeLists.txt
└── package.xml
对应CMakeLists.txt的典型配置:
cmake_minimum_required(VERSION 3.8) #指定构建项目所需的最低CMake版本。
project(my_ros2_package) #定义项目名称,并可设置项目语言(如C++、C等),
#查找并加载指定的ROS2或其他第三方依赖包,`REQUIRED`关键字表示必须找到该包,否则构建失败。将找到的功能包存储使用
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
#添加可执行节点talker,可执行节点talker由源码my_node.cpp编译而成。
# 'src/my_node.cpp' 是相对路径,是根据该功能包中的'CMakeLists.txt'文件的位置确定,
# 'CMakeLists.txt' 和src位于同一目录下。
add_executable(talker src/my_node.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
#可执行程序的执行必须依赖rclcpp 和 std_msgs 两个功能包
# 定义头文件路径(如果需要)
# '$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>'
#是指该示例中的include路径,即`~/ros2_ws/src/my_package/include`,
#是为了让 `#include "my_package/my_header.hpp"` 能生效。
#'$<INSTALL_INTERFACE:include>)'是指工作空间下的即
#`~/ros2_ws/install/my_package/include`,会让其他使用该包的功能包,
#在 `install` 目录下查找头文件。
target_include_directories(talker PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
# 安装节点到指定路径
#${PROJECT_NAME}是该功能包的名称,在这里为`my_package`。
ament_package()
# 完成ament构建系统设置
以上标准结构确保了ROS 2功能包的构建过程明确且易于维护。