ament_cmake是ROS 2中基于CMake的包的构建系统(特别是,它将用于大多数(如果不是全部的话)C/C++项目)。它是一组增强CMake并为包作者添加便利功能的脚本。了解CMake的基础知识会很有帮助,官方教程可以在这里找到。CMakecmake(1) — CMake 3.5.2 Documentation
可以在命令行上使用ros2 pkg create生成基本的CMake大纲
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 模板),客户端可以使用到这些文件。有关何时使用这些参数的示例,请参阅添加资源中的讨论。有关如何使用模板文件的详细信息,请参阅官方文档。除了添加到ament_package之外,您还可以添加到变量
${PROJECT_NAME}_CONFIG_EXTRAS和${PROJECT_NAME}_CONFIG_EXTRAS_POST效果相同。唯一的区别还是文件的添加顺序,总顺序如下:
有两个主要目标需要构建:分别由add_library和add_executable构建的库和可执行文件。
建议采用以下最佳做法:
target_include_directories(my_targetPUBLIC$$)
这会在生成时将文件夹${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_libraryEXPORT my_libraryTargetsLIBRARY DESTINATION libARCHIVE DESTINATION libRUNTIME DESTINATION binINCLUDES DESTINATION include
)
这里,我们假设文件夹include包含需要导出的头文件。请注意,不必将所有标头放入单独的文件夹中,只需要将客户端应该包含的标头放入即可。
下面是上面的片段中发生的事情:
install(DIRECTORY DESTINATION )
如上所述有两个附加功能可以使用,但对于基于目标的安装来说是多余的:
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)
target_compile_options(my_target PRIVATE -Wall)
由于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()
endif()
Ament包含CMake宏来简化GTests的设置。调用:
find_package(ament_cmake_gtest)
ament_add_gtest(some_test )
以添加GTest。然后,这是一个常规目标,可以与其他库(如项目库)链接。宏具有其他参数:
APPEND_ENV:附加环境变量。例如,您可以通过调用以下命令添加到ament前缀路径:
find_package(ament_cmake_gtest REQUIRED)
ament_add_gtest(some_test 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 TIMEOUT 120)
否则,默认工作目录为CMAKE_CURRENT_BINARY_DIR,CMake文档中对此进行了说明。
类似地,有一个CMake宏来设置包括GMock的GTest:
find_package(ament_cmake_gmock REQUIRED)
ament_add_gmock(some_test )