ROS使用(4)(ament_cmake)

介绍

ament_cmake是ROS 2中基于CMake的包的构建系统(特别是,它将用于大多数(如果不是全部的话)C/C++项目)。它是一组增强CMake并为包作者添加便利功能的脚本。了解CMake的基础知识会很有帮助,官方教程可以在这里找到。CMakecmake(1) — CMake 3.5.2 Documentation

基础内容

可以在命令行上使用ros2 pkg create生成基本的CMake大纲<package_name>。然后将基本构建信息收集到两个文件中:文件包和CMakeLists.txt。package.xml必须包含所有依赖项和一点元数据,以便colcon能够为您的包找到正确的构建顺序,在CI中安装所需的依赖项,并为bloom版本提供信息。CMakeLists.txt包含构建和打包可执行文件和库的命令

基础概要

ament包内容CMakeLists.txt的基础概要

cmake_minimum_required(Version 3.5)
project(my_project)

ament_package()

project的参数将是包名,并且必须与package.xml中的包名相同。
project设置是由ament_package()完成的,并且每个包必须恰好调用一次。ament_package()安装package.xml,用ament索引注册包,并安装CMake的配置(可能还有目标)文件,以便其他包可以使用find_package找到它。由于ament_package()从CMakeLists.txt中收集了大量信息,因此它应该是CMakeLists.txt中的最后一次调用。尽管可以在调用ament_package()之后调用install函数来复制文件和目录,但最后一次调用ament_package()要简单得多。

ament_package可以被赋予额外的参数:

  • CONFIG_EXTRAS:CMake 文件的列表(通过configure_file()扩展的.cmake 或 .cmake.in 模板),客户端可以使用到这些文件。有关何时使用这些参数的示例,请参阅添加资源中的讨论。有关如何使用模板文件的详细信息,请参阅官方文档。
  • CONFIG_EXTRAS_POST:与CONFIG_EXTRAS相同,但添加文件的顺序不同。虽然在为ament_export_* 调用生成文件之前包含CONFIG_EXTRAS文件,但在调用之后包含CONFIG_EXTRAS_POST中的文件。

除了添加到ament_package之外,您还可以添加到变量

${PROJECT_NAME}_CONFIG_EXTRAS和${PROJECT_NAME}_CONFIG_EXTRAS_POST效果相同。唯一的区别还是文件的添加顺序,总顺序如下:

  • 由CONFIG_EXTRAS添加的文件
  • 通过附加到${PROJECT_NAME}_CONFIG_EXTRAS添加的文件
  • 通过附加到${PROJECT_NAME}_CONFIG_EXTRAS_POST添加的文件
  • 由CONFIG_EXTRAS_POST添加的文件

添加文件和头文件

有两个主要目标需要构建:分别由add_libraryadd_executable构建的可执行文件

建议采用以下最佳做法:

  • 如果你正在构建一个库,把所有客户端可以使用的头文件放在include文件夹的一个子目录中,而所有其他文件(.c/.cpp和头文件不应该被导出)放在src文件夹中。
  • 只有cpp文件在add_library或add_executable的调用中被显式引用
  • 运行通过发现头文件的方法
target_include_directories(my_target
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)

这会在生成时将文件夹${CMAKE_CURRENT_SOURCE_DIR}/include中的所有文件添加到公共接口,并在安装时将include文件夹(相对于${CMAKE_INSTALL_DIR}中的所有文件添加到公共接口。

原则上,如果两个文件夹都被称为${CMAKE_CURRENT_SOURCE_DIR}和${CMAKE_INSTALL_DIR}的include和top-level,则无需在此处使用生成器表达式,但这是非常常见的。

添加对应依赖

有两种方法可以将包链接到新的依赖项。

第一种也是推荐的方法是使用ament宏ament_target_dependencies。例如,假设我们想要将my_target与线性代数库Eigen3相链接。

find_package(Eigen3 REQUIRED)
ament_target_dependencies(my_target Eigen3)

它包括必要的头文件以及项目正确找到它们的依赖项。它还将确保在使用覆盖工作区时,所有依赖项的包含目录都正确排序。

第二种方法是使用target_link_libraries

在现代CMake中推荐的方法是只使用目标,针对它们导出和链接。CMake目标是命名空间的,类似于C++。例如,Eigen3定义目标Eigen3::Eigen。
至少在Crystal Clemmys的目标名称在ament_target_dependencies宏中不受支持之前。有时需要调用target_link_libaries CMake函数。在Eigen3的示例中,调用应如下所示

find_package(Eigen3 REQUIRED)
target_link_libraries(my_target Eigen3::Eigen)

这也将包括必要的头文件、库及其依赖项,但与ament_target_dependencies相比,它在使用覆盖工作区时可能无法正确排序依赖项。

编译生成库

构建可重用库时,需要导出一些信息,以便下游包轻松使用它。

ament_export_targets(my_libraryTargets HAS_LIBRARY_TARGET)
ament_export_dependencies(some_dependency)

install(
  DIRECTORY include/
  DESTINATION include
)

install(
  TARGETS my_library
  EXPORT my_libraryTargets
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

这里,我们假设文件夹include包含需要导出的头文件。请注意,不必将所有标头放入单独的文件夹中,只需要将客户端应该包含的标头放入即可。

下面是上面的片段中发生的事情:

  • ament_export_targets宏导出CMake的目标。这是允许库的客户端使用target_link_libraries(client my_library::my_library)语法所必需的。ament_export_targets可以接受install调用中名为EXPORT的任意目标列表和附加选项HAS_LIBRARY_TARGET,后者将潜在库添加到环境变量中。
  • ament_export_dependencies将依赖项导出到下游包。这是必要的,这样库的用户就不必为那些依赖项也调用find_package
  • 第一个install命令安装客户端应该可用的头文件。
  • 最后一个大型install命令安装库。归档文件和库文件将导出到lib文件夹,运行时二进制文件将安装到bin文件夹,安装头文件的路径为include。
  • 关于include目录,install命令只是向CMake添加信息,它实际上并不安装include文件夹。这是通过install(DIRECTORY <dir> DESTINATION <dest>) 如上所述
  • install调用的EXPORT表示法需要特别注意:它为my_library目标安装CMake文件。它的名称与ament_export_targets中的参数完全相同,也可以像库一样命名。但是,这将禁止ament_target_dependencies方法包含库。为了获得充分的灵活性,建议在导出目标前面加上类似于<target>目标。
  • 所有安装路径都相对于CMAKE_INSTALL_PREFIX,该路径已由colcon/ament正确设置

有两个附加功能可以使用,但对于基于目标的安装来说是多余的:

ament_export_include_directories(include)
ament_export_libraries(my_library)

第一个宏标记导出的包含目录的目录(这是通过目标安装调用中的INCLUDES DESTINATION实现的)。第二个宏标记已安装库的位置(这是通过调用ament_export_targets中的HAS_LIBRARY_TARGET参数完成的)。

一些宏可以接受不同类型的参数用于非目标导出,但是由于现代Make推荐的方法是使用目标,所以我们在这里不讨论它们。这些选项的文档可以在源代码中找到。
 

编译器和链接器选项

ROS 2的目标编译器符合C++14和C99标准,直到Crystal Clemmysys。更新的版本可能是未来的目标,并在这里引用。因此,通常设置相应的CMake标志:

if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

若要保持代码整洁,编译器应该对有问题的代码抛出警告,并且应该修复这些警告。
建议至少包括以下警告级别:

尽管现代的CMake建议在目标基础上添加编译器标志,即调用

target_compile_options(my_target PRIVATE -Wall)
  • 对于Visual Studio,将保留默认的W1警告
  • 对于GCC和Clang:-Wall -Wextra -Wpedantic是必需的,-Wshadow -Werror是可取的(后者产生警告错误)。
target_compile_options(my_target PRIVATE -Wall)

生成库在Windows中

由于Linux、Mac和Windows都是官方支持的平台,为了产生最大的影响,任何软件包都应该构建在Windows上。Windows库格式强制符号可见性:每个应该从客户端使用的符号都必须由库显式导出(数据符号需要隐式导入)。

为了保持与Clang和GCC版本的兼容性,建议使用GCC wiki中的逻辑。要将其用于名为my_library的软件包:

  • 将链接中的逻辑复制到名为visibility_control.hpp的头文件中

  • 将DLL替换为MY_LIBRARY(有关示例,请参见rviz_rendering的可见性控制)。

  • 对所有需要导出的符号(即类或函数)使用宏“MY_LIBRARY_PUBLIC”。

  • 在项目CMakeLists.txt中用途:

target_compile_definitions(my_library PRIVATE "MY_LIBRARY_BUILDING_LIBRARY")

测试

为了将测试与使用colcon构建库分开,请将所有对链接器和测试的调用包装在一个条件中:

if(BUILD_TESTING)
  find_package(ament_cmake_gtest REQUIRED)
  ament_add_gtest(<tests>)
endif()

Ament包含CMake宏来简化GTests的设置。调用:

find_package(ament_cmake_gtest)
ament_add_gtest(some_test <test_sources>)

以添加GTest。然后,这是一个常规目标,可以与其他库(如项目库)链接。宏具有其他参数:

APPEND_ENV:附加环境变量。例如,您可以通过调用以下命令添加到ament前缀路径:

find_package(ament_cmake_gtest REQUIRED)
ament_add_gtest(some_test <test_sources>
  APPEND_ENV PATH=some/addtional/path/for/testing/resources)

APPEND_LIBRARY_DIRS:附加库,以便链接器可以在运行时找到它们。这可以通过设置环境变量(如Windows上的PATH和Linux上的LD_LIBRARY_PATH)来实现,但这会使调用特定于平台。
ENV:设置环境变量(语法与APPEND_ENV相同)。
TIMEOUT:设置测试超时,单位为秒。GTests的默认值为60秒。例如:

ament_add_gtest(some_test <test_sources> TIMEOUT 120)
  • SKIP_TEST:跳过此测试(将在控制台输出中显示为“通过”)。
  • 跳过链接主库:不要链接到GTest。
  • 工作目录:设置测试的工作目录。

否则,默认工作目录为CMAKE_CURRENT_BINARY_DIR,CMake文档中对此进行了说明。
类似地,有一个CMake宏来设置包括GMock的GTest:

find_package(ament_cmake_gmock REQUIRED)
ament_add_gmock(some_test <test_sources>)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值