跳到主要内容

CMakeLists.txt文件的深入理解

在 ROS2 开发过程中,CMakeLists.txt 文件至关重要。ROS2 功能包通常包含节点、库、头文件及其他资源,这些内容的构建和组织需要借助CMake实现。具体来说,ROS2 使用 CMakeLists.txt 完成以下任务:

  • 查找和管理ROS 2的依赖项,如rclcppstd_msgs等。
  • 编译节点的可执行文件和动态链接库。
  • 配置节点运行时所需的路径和环境。
  • 定义安装路径,以便节点和其他资源能够被 ROS2 系统识别和使用。 通过规范的 CMakeLists.txt 配置,开发者能够确保功能包的构建过程清晰且易于维护,使 ROS2 项目更加稳定、可扩展和易于移植。

ROS 2中的路径

ROS2 中的路径问题是初学者经常感到困惑的地方。下面从概念到具体实践,帮助读者更好地理解 ROS2 和CMakeLists.txt 中的路径问题。

ROS2 涉及的路径主要分为以下几种:

  1. 源码路径(Source Path)

    • 功能包源文件、头文件所在的位置,通常位于工作空间中,如: ~/ros2_ws/src/my_package/ 其中,my_package 表示创建的功能包的名称。
  2. 编译路径(Build Path)

    • 功能包编译时生成的临时文件,如编译中间文件 .o 等,通常在: ~/ros2_ws/build/my_package/
  3. 安装路径(Install Path)

    • 功能包编译完成后生成的可执行文件、动态库、头文件安装位置,位于: ~/ros2_ws/install/my_package/

    • 具体如:

      • 可执行文件:~/ros2_ws/install/my_package/lib/my_package/
      • 头文件:~/ros2_ws/install/my_package/include/
  4. 系统路径与环境变量(如ROS_PACKAGE_PATH)

    • ROS2 运行时通过环境变量定位功能包路径,加载安装目录的 setup.bash 使得 ROS2 能够找到功能包:

    source ~/ros2_ws/install/setup.bash

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功能包的构建过程明确且易于维护。