ROS/CMakeLists.txt书写规则

  • 1. 概述
  • 2. 所有结构和指令
  • 3. CMake版本
  • 4. 功能包(项目)名称
  • 5. 查找依赖的CMake包
  • 5.1 find_package()做了什么工作
  • 5.2 为什么Catkin Packages被声明Components
  • 5.3 Boost
  • 6. catkin_package()
  • 7. 指定构建对象
  • 7.1 目标命名
  • 7.2 自定义输出目录
  • 7.3 包含路径和库路径
  • 7.3.1 include_directories()
  • 7.3.2 link_directories()
  • 7.4 可执行目标
  • 7.5 链接库目标
  • 7.6 target_link_libraries
  • 8. Messages, Services, and Action Targets
  • 8.1 重要的前提
  • 8.2 例子


1. 概述

文件CMakeLists.txt是用于构建软件包的CMake构建系统的输入。任何符合cmake的包都包含一个或多个CMakeLists.txt文件,该文件描述了如何构建代码以及将其安装到何处。用于catkin项目的CMakeLists.txt文件是一个标准的普通CMakeLists.txt文件,带有一些额外的约束。

2. 所有结构和指令

我们的CMakeLists.txt必须遵照如下格式,否则我们的功能包不能正常被构建。

  1. Required CMake Version (cmake_minimum_required)
  2. Package Name (project())
  3. Find other CMake/Catkin packages needed for build (find_package())
  4. Enable Python module support (catkin_python_setup())
  5. Message/Service/Action Generators (add_message_files(), add_service_files(), add_action_files())
  6. Invoke message/service/action generation (generate_messages())
  7. Specify package build info export (catkin_package())
  8. Libraries/Executables to build (add_library()/add_executable()/target_link_libraries())
  9. Tests to build (catkin_add_gtest())
  10. Install rules (install())

3. CMake版本

所有CMakeLists.txt都需要在开头声明版本

cmake_minimum_required(VERSION 2.8.3)

4. 功能包(项目)名称

接着需要声明功能包或者说该项目的名称

project(robot_brain)

请注意,在 CMake 中,我们可以在 CMake 脚本中任何需要的地方使用变量 ${PROJECT_NAME} 来引用项目名称。

5. 查找依赖的CMake包

然后,我们需要使用 CMake find_package 函数指定需要找到哪些其他 CMake 包来构建我们的项目。 总是至少有一个对 catkin 的依赖:

find_package(catkin REQUIRED)

如果你的项目依赖其他的wet包,它们会自动变成catkin的组件(就CMake而言)。 而不是在这些包上使用 find_package 。 例如,如果您使用软件包 nodelet

find_package(catkin REQUIRED COMPONENTS nodelet)

我们也可以这样实现同样效果:

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

5.1 find_package()做了什么工作

如果 CMake 通过 find_package 找到一个包,则会导致创建多个 CMake 环境变量,这些变量提供有关找到的包的信息。 这些环境变量可以稍后在 CMake 脚本中使用。 环境变量描述了包导出头文件的位置、源文件的位置、包依赖的库以及这些库的路径。 名称始终遵循 <PACKAGE NAME>_<PROPERTY> 的格式:

  • <NAME>_FOUND - 如果找到库,则设置为true,否则为false
  • <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES - 包导出的包含路径
  • <NAME>_LIBRARIES or <NAME>_LIBS - 包导出的库路径
  • <NAME>_DEFINITIONS - ?

5.2 为什么Catkin Packages被声明Components

实际上,Catkin的包并不是catkin的真正组成部分。而catkin采用CMake的组件功能,主要是为了节省打字时间。
将Catkin的包作为一种组件看待是很有好处的,当你找到包的时候,各种以catkin为前缀的添加到CMakeLists.txt中了。以前述nodelet包为例:

find_package(catkin REQUIRED COMPONENTS nodelet)

这意味着由nodelet导出的包含路径、库路径等,也添加到catkin前缀的变量中。例如,catkin_INCLUDE_DIRS不仅包含catkininclude路径,还包含了nodelet,这在后面会用到。

我们也可以自主选择仅查找nodelet包

find_package(nodelet)

之后的包含路径、库等变量分别为nodelet_INCLUDE_DIRS, nodelet_LIBRARIES

5.3 Boost

如果使用C ++和Boost,则需要用find_package()来找Boost库,并指定Boost中的组件。 例如,如果想使用Boost线程,就可以写成:

find_package(Boost REQUIRED COMPONENTS thread)

6. catkin_package()

catkin_package() 是 catkin 提供的 CMake 宏。 这是为构建系统指定特定于 catkin 的信息所必需的,该信息又用于生成 pkg-config 和 CMake 文件。

该函数必须在add_library()add_executable()之前调用。该函数有5个可选参数:

  • INCLUDE_DIRS - 导出功能包的include路径
  • LIBRARIES - 导出项目中的库
  • CATKIN_DEPENDS - 该项目依赖的其他catkin项目
  • DEPENDS - 该项目依赖的其他非catkin项目
  • CFG_EXTRAS - 其他配置选项

例如:

catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)
  • 这表明功能包中的文件夹“include”是导出的头文件所在的位置。
  • CMake 环境变量 ${PROJECT_NAME} 根据之前传递给 project() 函数的任何内容,在这种情况下它将是“robot_brain”。
  • “roscpp”+“nodelet”是构建/运行这个包需要存在的包。
  • “eigen”+“opencv”是构建/运行这个包需要存在的系统依赖项。

7. 指定构建对象

构建目标可以有多种形式,但通常主要有以下两种:

  • 执行文件目标 - 可以运行的程序
  • 库目标 - 可在构建和/或运行时给可执行目标使用的库

7.1 目标命名

重点:catkin中的构建目标的命名必须唯一。不过,构建目标命名的唯一性只能在CMake内部进行。可以使用set_target_properties()函数将目标重命名为其他目标,例如

set_target_properties(rviz_image_view
                      PROPERTIES OUTPUT_NAME image_view
                      PREFIX "")

这时就将rviz_image_view的名称更改为image_view了。

7.2 自定义输出目录

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

例子:

set_target_properties(python_module_library
  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})

7.3 包含路径和库路径

在编译之前,必须告诉编译器在哪里可以找到所需的头文件和库:

  • Include Paths - 头文件
  • Library Paths - 构建对象需要用到的库
include_directories(<dir1>, <dir2>, ..., <dirN>)

link_directories(<dir1>, <dir2>, ..., <dirN>)

7.3.1 include_directories()

include_directories的参数应该是由find_package调用生成的*_INCLUDE_DIRS变量,以及需要包含的其他目录。如果你使用catkin和Boost,你的include_directories()调用应该是这样的:

include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})

注:第一个参数“include”表示包中的include/目录也是路径的一部分

7.3.2 link_directories()

可以使用CMake的 link_directories()函数添加额外的库路径,但是,不建议这样做。所有catkin和CMake包在find_packaged时会自动添加它们的链接信息。只需在target_link_libraries()中链接库:

link_directories(~/my_libs)

7.4 可执行目标

要指定一个必须构建的可执行目标,我们必须使用add_executable()函数:

add_executable(myProgram src/main.cpp src/some_file.cpp src/another_file.cpp)

这将构建一个名为myProgram的目标可执行文件,它由3个源文件构建:src/main.cppsrc/some_file.cppsrc/another_file.cpp

7.5 链接库目标

add_library()函数用于指定要构建的库。默认情况下,catkin构建链接库:

add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})

7.6 target_link_libraries

target_link_libraries()指定可执行目标链接的库。 通常在add_executable()调用之后完成。 如果找不到ros,则添加$ {catkin_LIBRARIES}
Syntax:

target_link_libraries(<executableTargetName>, <lib1>, <lib2>, ... <libN>)

Example:

add_executable(foo src/foo.cpp)
add_library(moo src/moo.cpp)
target_link_libraries(foo moo)  -- This links foo against libmoo.so

请注意,在大多数情况中不需要使用link_directories(),因为find_package()自动导入。

8. Messages, Services, and Action Targets

ROS中的消息(.msg)、服务(.srv)和操作(.action)文件在被ROS包构建和使用之前需要一个特殊的预处理程序构建步骤。这些宏的作用是生成特定于编程语言的文件,这样人们就可以使用他们所选择的编程语言来使用消息、服务和操作。构建系统将使用所有可用的生成器(如gencpp、genpy、genlisp等)来生成绑定。

这里提供了三个宏来分别处理消息、服务和操作:

  • add_message_files
  • add_service_files
  • add_action_files

而在之后必须在下面写下面宏,catkin build时候会在catkin_ws/devel/include文件夹中生成对应的头文件:

generate_messages()

8.1 重要的前提

  1. 这些宏必须出现在catkin_package()宏之前,以便生成能够正确工作。
find_package(catkin REQUIRED COMPONENTS ...)
 add_message_files(...)
 add_service_files(...)
 add_action_files(...)
 generate_messages(...)
 catkin_package(...)
 ...
  1. catkin_package()宏中写上CATKIN_DEPENDS message_runtime
catkin_package(
 ...
 CATKIN_DEPENDS message_runtime ...
 ...)
  1. 并且在find_package()写上message_generation组件:
find_package(catkin REQUIRED COMPONENTS message_generation)
  1. package.xml文件中加上
<build_depend>message_generation</build_depend>
    <run_depend>message_runtime</run_depend>
  1. 如果构建的对象依赖于其他需要构建消息/服务/响应的构建对象,则需要向目标catkin_EXPORTED_TARGETS添加明确的依赖关系,以便它们以正确的顺序构建。 这种情况比较常用,除非您的包真的不使用ROS的任何部分。 但这种依赖关系不能自动传递。 (some_target是由add_executable()设置的目标的名称):
add_dependencies(some_target ${catkin_EXPORTED_TARGETS})
  1. 如果需要构建消息或服务的包以及使用这些消息和/或服务的可执行文件,则需要为自动生成的消息目标创建明确的依赖关系,以便以正确的顺序构建它们。 (some_target是由add_executable()设置的目标的名称):
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
  1. 如果您的catkin包满足上述两个条件,则需要添加以下两个依赖关系,即:
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

8.2 例子

现在有两个依赖于std_msgssensor_msgs的消息MyMessage1.msgMyMessage2.msg,还有一个自定义服务MyService.srvmessage_program是使用这些消息和服务的指令,以及生成不使用自定义消息、服务的程序do_not_use_local_messages_programcatkin包,那么CMakeLists.txt应该写成:

# 构建时依赖项
find_package(catkin REQUIRED COMPONENTS          
             message_generation 
             std_msgs 

# 声明要构建哪些消息
add_message_files(FILES
                  MyMessage1.msg
                  MyMessage2.msg)
                  
# 声明构建哪些服务
add_service_files(FILES
                  MyService.srv)

# 声明生成上述消息、服务需要依赖的消息以及服务
generate_messages(DEPENDENCIES 
                    std_msgs 
                    sensor_msgs)

# 声明运行时依赖项
catkin_package(CATKIN_DEPENDS 
                message_runtime 
                std_msgs sensor_msgs)

# 声明构建生成的可执行文件名称以及依赖项
add_executable(message_program src/main.cpp)
add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

# 声明构建不需要使用自定义消息、服务的可执行文件
add_executable(does_not_use_local_messages_program src/main.cpp)
add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})

如果要构建响应,则在包中action目录中创建有一个名为MyAction.action的文件,则必须将actionlib_msgs添加到使用catkin进行find_package的组件列表中,然后在CMakeList.txt中加上下面两行:

add_action_files(FILES
                 MyAction.action)

此外,该包必须具有对actionlib_msgs的构建依赖。