修改自 ros 官方 wiki

http://wiki.ros.org/catkin/CMakeLists.txt

CMakeLists.txtCMake 构建系统的输入,用于构建软件包。catkinCMakeLists.txt 带有一些其它的约束

总体结构

  1. 所需的 CMake 版本。( cmake_minimum_required

  2. 包名称。( project

  3. 查找构建所需的 CMake或 Catkin 软件包。( find_package

  4. 启动 Python 模块支持。( catkin_python_setup

  5. 消息、服务、动作生成器。( add_message_filesadd_service_filesadd_action_files

  6. 调用消息、服务、动作生成。( generate_messages

  7. 指定包构建信息导出。( catkin_package

  8. 添加要构建的库或可执行文件。( add_libraryadd_executabletarget_link_libraries

  9. 测试构建。( catkin_add_gtest

  10. 安装规则。( 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
2
3
4
find_package(
catkin REQUIRED COMPONENTS
roscpp
)

也可以

1
2
find_package(catkin REQUIRED)
find_package(roscpp 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 个可选参数:

  1. INCLUDE_DIRS:导出包的包含路径
  2. LIBRARIES:项目中导出的库
  3. CATKIN_DEPENDS:此项目依赖的其他 Catkin 项目
  4. DEPENDS:此项目依赖的非 Catkin CMake 项目
  5. CFG_EXTRAS:其他配置选项

详细参数参见文档

指定构建目标

构建目标通常有多种形式,但是通常它们代表以下两种可能性之一:

  • 可执行程序
  • 库文件

目标命名

需要注意的是,Catkin 中构建目标名称必须唯一,而与构建、安装到的文件夹无关,这也是 CMake 的要求,但是,构建目标名称的唯一性仅仅只是在 CMake 内部才是必须的。我们可以使用set_target_properties()函数将目标重命名为其他目标:

1
2
3
4
5
set_target_properties(
rviz_image_view
PROPERTIES OUTPUT_NAME image_view
PREFIX ""
)

在构建过程中,将目标rviz_image_view的名称更改为image_view

自定义输入目录

通常将可执行程序和库文件的默认输出目录设置为合理的值,但在某些情况下必须对其进行自定义。例如,包含 Python 绑定的库必须放置在其他文件夹中才能在 Python 中导入

1
2
3
4
5
set_target_properties(
python_module_library
PROPERTIES LIBRARY_OUTPUT_DIRECTORY
${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}
)

包含路径和库路径

在指定目标之前,你需要指定在哪里可以找到所需的资源,特别是头文件和库文件

  • 包含路径:在哪里找到需要的头文件(在 C/C++ 中最常见)
1
include_directories(<dir1> <dir2> ... <dirN>)
  • 链接路径:在哪里可以找到库文件
1
link_directories(<dir1> <dir2> ... <dirN>)

include_directories()

include_directories的参数因为 find_package 调动生成的*_INCLUDE_DIRS变量以及需要包含的任何其他目录。如果你需要使用 CatkinBoost,则include_directories()调用应如下所示:

1
2
3
4
5
include_directories(
include
${Boost_INCLUDE_DIRS}
${catkin_INCLUDE_DIRS}
)

link_directories() 函数可用于添加其他库的路径,但是不建议这么做。当所有 CatkinCMake 的包被 find_package 时,会自动添加其链接信息,只需要使用target_link_libraries()链接即可。

可执行程序

要指定必须构建的可执行程序,我们必须使用add_executable()函数。

1
add_executable(main src/main.cc)

这将构建一个名为 main 的可执行程序,该可执行程序由 1 个源文件构建:src/main.cc

库文件

使用add_library()函数来构建库文件。默认情况下,Catkin 构建共享库。

1
2
3
4
add_library(
${PROJECT_NAME}
${${PROJECT_NAME}_SRCS}
)

使用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
2
3
4
5
6
7
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
...
  • 你的 catkin_package()必须对message_runtime具有CATKIN_DEPENDS依赖性
1
2
3
4
5
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
ros
  • 你必须将message_generation添加到 find_package()中,无论是单独使用还是作为 Caktin 组件使用。
1
2
3
4
find_package(
catkin REQUIRED COMPONENTS
message_generation
)
  • 你的 package.xml文件中必须包含构建时依赖message_generation运行时依赖message_runtime,如果依赖关系是其他包中传递过来的,则没有必要。
1
2
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
  • 如果你有一个目标(甚至是过渡性的)依赖于需要构建消息、服务、操作的其他目标,则需要添加对目标的catkin_EXPORTED_TARGETS的显式依赖关系,以便以正确的顺序构建它们。这种情况几乎总是适用的,除非你的程序包确实不适用 ROS 的任何部分,这种依赖性不能自动传播。(some_target是有add_executable()设置的目标名称):
1
2
3
4
add_dependencies(
some_target
${catkin_EXPORTED_TARGETS}
)
  • 如果你具有用于构建消息和服务的程序包,以及适用这些消息和服务的可执行文件,则需要在自动生成消息目标上创建显示依赖项,以便用正确的顺序构建它们。(some_target是有add_executable()设置的目标名称):
1
2
3
4
add_dependencies(
some_target
${${PROJECT_NAME}_EXPORTED_TARGETS}
)
  • 如果你的包满足上述两种情况,则你需要添加两个显式依赖:
1
2
3
4
5
add_dependencies(
some_target
${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS}
)

启用 Python 模块支持

如果你的 ros 包提供了一些 Python 模块,则应创建 setup.py 文件并调用

1
catkin_python_setup()

注意:必须在调用generate_messages()catkin_package()之前调用

单元测试

Catkin有一个特定的宏,用于处理基于 gtest 的单元测试,称为 catkin_add_target()

1
2
3
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(myUnitTest test/utest.cpp)
endif()

评论