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必须遵照如下格式,否则我们的功能包不能正常被构建。
- Required CMake Version (cmake_minimum_required)
- Package Name (project())
- Find other CMake/Catkin packages needed for build (find_package())
- Enable Python module support (catkin_python_setup())
- Message/Service/Action Generators (add_message_files(), add_service_files(), add_action_files())
- Invoke message/service/action generation (generate_messages())
- Specify package build info export (catkin_package())
- Libraries/Executables to build (add_library()/add_executable()/target_link_libraries())
- Tests to build (catkin_add_gtest())
- 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
不仅包含catkin
的include
路径,还包含了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.cpp
、src/some_file.cpp
和src/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 重要的前提
- 这些宏必须出现在
catkin_package()
宏之前,以便生成能够正确工作。
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
...
- 在
catkin_package()
宏中写上CATKIN_DEPENDS message_runtime
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
- 并且在
find_package()
写上message_generation
组件:
find_package(catkin REQUIRED COMPONENTS message_generation)
-
package.xml
文件中加上
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
- 如果构建的对象依赖于其他需要构建消息/服务/响应的构建对象,则需要向目标
catkin_EXPORTED_TARGETS
添加明确的依赖关系,以便它们以正确的顺序构建。 这种情况比较常用,除非您的包真的不使用ROS的任何部分。 但这种依赖关系不能自动传递。 (some_target
是由add_executable()
设置的目标的名称):
add_dependencies(some_target ${catkin_EXPORTED_TARGETS})
- 如果需要构建消息或服务的包以及使用这些消息和/或服务的可执行文件,则需要为自动生成的消息目标创建明确的依赖关系,以便以正确的顺序构建它们。 (
some_target
是由add_executable()
设置的目标的名称):
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
- 如果您的catkin包满足上述两个条件,则需要添加以下两个依赖关系,即:
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
8.2 例子
现在有两个依赖于std_msgs
和sensor_msgs
的消息MyMessage1.msg
和MyMessage2.msg
,还有一个自定义服务MyService.srv
, message_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的构建依赖。