修改自 ros 官方 wiki
CMakeLists.txt
是 CMake
构建系统的输入,用于构建软件包。catkin
的
CMakeLists.txt
带有一些其它的约束
总体结构
-
所需的 CMake 版本。(
cmake_minimum_required
) -
包名称。(
project
) -
查找构建所需的 CMake或 Catkin 软件包。(
find_package
) -
启动 Python 模块支持。(
catkin_python_setup
) -
消息、服务、动作生成器。(
add_message_files
,add_service_files
,add_action_files
) -
调用消息、服务、动作生成。(
generate_messages
) -
指定包构建信息导出。(
catkin_package
) -
添加要构建的库或可执行文件。(
add_library
,add_executable
,target_link_libraries
) -
测试构建。(
catkin_add_gtest
) -
安装规则。(
install
)
CMake 版本
每个 catkin CMakeLists.txt 文件必须以所需的 CMake 版本开头。Catkin 需要的版本是 2.8.3 或更高
1 |
cmake_minimum_required(VERSION 2.8.3) |
包名
必须指定其包名称。假设我们正在制作一个名为 hello_ros
的包。
1 |
project(hello_ros) |
注意:在 CMake 中,可以在以后任意需要的地方使用变量${PROJECT_NAME}
在 CMake 脚本中的任何位置应用项目名称。
查找依赖的 CMake 软件包
我们需要使用 find_package 函数来指定找到哪些其他 CMake 软件包来构建我们的项目。Catkin 至少需要一个依赖
1 |
find_package(catkin REQUIRED) |
术语:
wet packages(湿包),用 Catkin 构建的包
dry packages(干包),用 rosbuild 构建的包
如果项目依赖于其他的湿包,则可以将这些包指定为组件,而不是在这些包上使用 find_package
,它使工作更加的轻松。例如使用了 roscpp
1 |
find_package( |
也可以
1 |
find_package(catkin REQUIRED) |
显然,第一种方式更为方便
find_package() 有什么作用
如果 CMake通过 find_package() 找到一个包,它会自动创建几个 CMake 的环境变量,这些变量提供有关找到的包的信息。这些环境变量可以在稍后的 CMake
脚本中使用。这些环境变量描述了软件包导出的头文件在哪里,源文件在哪里,软件包所依赖的库以及这些库的路径。名称始终遵循<PACKAGE NAME>_<PROPERTY>
的约定:
- <NAME>_FOUND,如果找到该值为 true,否则为 false
- <NAME>_INCLUDE_DIRS 或<NAME>_INCLUDES,包导出的包含路径
- <NAME>_LIBRARIES或<NAME>_LIBS,包导出的库
- <NAME>_DEFINITIONS
为什么将 Catkin 包指定为组件(COMPONENTS)
Catkin 包在 Cakin 中并不是真正的组件,但是在 Catkin 设计中利用了 CMake 这个组件功能,以节省大量的编辑时间。
对于catkin包,如果将find_package用作catkin的组件,则这是有利的,因为使用catkin_
前缀创建了一组环境变量。
catkin_package()
catkin_package()
是 catkin 提供的 CMake
宏。这是向构建系统指定特定于 Catkin 的信息所必需的,而构建系统又将其用于生成
pkg-config 和 CMake 文件
必须在使用add_library()
和add_executable()
声明任何目标之前调用此函数。
该函数具有 5 个可选参数:
INCLUDE_DIRS
:导出包的包含路径LIBRARIES
:项目中导出的库CATKIN_DEPENDS
:此项目依赖的其他 Catkin 项目DEPENDS
:此项目依赖的非 Catkin CMake 项目CFG_EXTRAS
:其他配置选项
指定构建目标
构建目标通常有多种形式,但是通常它们代表以下两种可能性之一:
- 可执行程序
- 库文件
目标命名
需要注意的是,Catkin 中构建目标名称必须唯一,而与构建、安装到的文件夹无关,这也是 CMake 的要求,但是,构建目标名称的唯一性仅仅只是在 CMake
内部才是必须的。我们可以使用set_target_properties()
函数将目标重命名为其他目标:
1 |
set_target_properties( |
在构建过程中,将目标rviz_image_view
的名称更改为image_view
自定义输入目录
通常将可执行程序和库文件的默认输出目录设置为合理的值,但在某些情况下必须对其进行自定义。例如,包含 Python 绑定的库必须放置在其他文件夹中才能在 Python 中导入
1 |
set_target_properties( |
包含路径和库路径
在指定目标之前,你需要指定在哪里可以找到所需的资源,特别是头文件和库文件
- 包含路径:在哪里找到需要的头文件(在 C/C++ 中最常见)
1 |
include_directories(<dir1> <dir2> ... <dirN>) |
- 链接路径:在哪里可以找到库文件
1 |
link_directories(<dir1> <dir2> ... <dirN>) |
include_directories()
include_directories
的参数因为 find_package
调动生成的*_INCLUDE_DIRS变量以及需要包含的任何其他目录。如果你需要使用 Catkin
和
Boost
,则include_directories()
调用应如下所示:
1 |
include_directories( |
link_directories()(不建议使用)
link_directories()
函数可用于添加其他库的路径,但是不建议这么做。当所有 Catkin
和 CMake
的包被
find_package
时,会自动添加其链接信息,只需要使用target_link_libraries()
链接即可。
可执行程序
要指定必须构建的可执行程序,我们必须使用add_executable()
函数。
1 |
add_executable(main src/main.cc) |
这将构建一个名为 main 的可执行程序,该可执行程序由 1 个源文件构建:src/main.cc
库文件
使用add_library()
函数来构建库文件。默认情况下,Catkin
构建共享库。
1 |
add_library( |
target_link_libraries
使用target_link_libraries()
函数来指定可执行程序需要链接到的库文件。通常add_executable()
调用之后完成此操作。如果找不到
ros
,请添加${catkin_LIBRARIES}
1 |
target_link_libraries(<executableTargetName> <lib1> <lib2> ... <libN>) |
注意:在大多数情况下都不需要使用 link_directories()
,因为该信息是通过 find_package()
自动获取的
消息、服务和动作
ROS 中的消息(.msg)
,服务(.srv)
和动作(.action)
文件需要特殊的预处理器构建步骤,然后才能由 ROS
包构建和使用。这些宏的目的是生成特定编程语言的文件,以便人们可以选择其他编程语言来利用消息、服务和操作。构建系统将使用所有可用的生成器(gencpp, genpy, genlisp等)
生成绑定。
Catkin 提供了三个宏来分别处理消息、服务和动作:
add_message_files
add_service_files
add_action_files
然后,必须在使用完这些宏之后调用该宏以生成特定的文件:
1 |
generate_messages() |
重要的前提条件和约束
- 以下宏必须在 catkin_package()宏之前出现,以便正常工作
1 |
find_package(catkin REQUIRED COMPONENTS ...) |
- 你的
catkin_package()
必须对message_runtime
具有CATKIN_DEPENDS
依赖性
1 |
catkin_package( |
- 你必须将
message_generation
添加到find_package()
中,无论是单独使用还是作为 Caktin 组件使用。
1 |
find_package( |
- 你的
package.xml
文件中必须包含构建时依赖message_generation
和运行时依赖message_runtime
,如果依赖关系是其他包中传递过来的,则没有必要。
1 |
<build_depend>message_generation</build_depend> |
-
如果你有一个目标(甚至是过渡性的)依赖于需要构建消息、服务、操作的其他目标,则需要添加对目标的
catkin_EXPORTED_TARGETS
的显式依赖关系,以便以正确的顺序构建它们。这种情况几乎总是适用的,除非你的程序包确实不适用 ROS 的任何部分,这种依赖性不能自动传播。(some_target
是有add_executable()
设置的目标名称):
1 |
add_dependencies( |
-
如果你具有用于构建消息和服务的程序包,以及适用这些消息和服务的可执行文件,则需要在自动生成消息目标上创建显示依赖项,以便用正确的顺序构建它们。(
some_target
是有add_executable()
设置的目标名称):
1 |
add_dependencies( |
- 如果你的包满足上述两种情况,则你需要添加两个显式依赖:
1 |
add_dependencies( |
启用 Python 模块支持
如果你的 ros 包提供了一些 Python 模块,则应创建 setup.py 文件并调用
1 |
catkin_python_setup() |
注意:必须在调用generate_messages()
和catkin_package()
之前调用
单元测试
Catkin有一个特定的宏,用于处理基于 gtest 的单元测试,称为 catkin_add_target()
1 |
if(CATKIN_ENABLE_TESTING) |