文章只是个人学习过程中学习笔记,主要参考ROS教程。
目录
- 1、概述
- 2、CMakeLists.txt文件
- 2.1 遵循的格式和顺序
- 2.2 文件解析
- 2.3 find_package()
- 2.4 catkin_package()
1、概述
CMakeLists.txt
是用于编译软件包的CMake编译系统的输入文件,几乎可以在任何文本编辑器中进行编辑。该文件描述了如何编译代码以及安装到何处。用于catkin
项目的CMakeLists.txt
文件是一个标准的附带一些额外约束的vanilla
CMakeLists.txt
文件。
catkin
是ROS官方自定义的编译系统,它的原理与流程与CMake
很相似,和老版本rosbuild相比,支持更好的可移植性(catkin结合了可移植性好的CMake和python)以及交叉编译。(在ROS2使用的编译工具是ament
,它是由catkin
优化迭代而来的版本)
CMake语言由 注释(comments)
、命令(commands)
和 变量(variables)
组成。
CMake相关文档:https://cmake.org/documentation/
2、CMakeLists.txt文件
2.1 遵循的格式和顺序
CMakeLists.txt
文件必须遵循以下格式,否则软件包将无法正确构建。配置中的顺序确实很重要。CMakeLists.txt
的基本语法都还是CMake,而catkin在其中添加了少量的宏(见new)。
顺序 | 语法 | 功能 | 备注 |
---|
1 | cmake_minimum_required | 所需的 CMake 版本 | – |
2 | project() | 包名 | – |
3 | find_package() | 查找编译所需的其他 CMake/Catkin 包 | – |
4 | catkin_python_setup() | 启用 Python 模块支持 | new |
5 | add_message_files() | 添加在msg 文件夹中自定义的*.msg 消息文件 | new |
| add_service_files() | 添加在srv 文件夹中自定义的*.srv 服务文件 | new |
| add_action_files() | 添加在avtion 文件夹中自定义的*.action 动作文件 | new |
6 | generate_messages() | 调用消息 /服务 /动作 生成特定语言的接口文件 | new |
7 | catkin_package() | 指定包构建信息导出 | new |
8 | add_library() | 生成库文件 | – |
| add_dependencies() | 定义目标文件依赖于其他文件,确保其他目标已被构建 | – |
| add_executable() | 生成可执行的二进制文件 | – |
| target_link_libraries() | 指定可执行目标链接的库 | – |
9 | catkin_add_gtest() | 要编译的测试 | new |
10 | install() | 安装规则 | – |
2.2 文件解析
cmake_minimum_required(VERSION 3.0.2)
project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
catkin_package(
)
include_directories(
${catkin_INCLUDE_DIRS}
)
2.3 find_package()
如果 CMake 通过find_package()
找到一个package
,则会创建几个 CMake 环境变量,这些变量提供有关找到的package
的信息。这些环境变量可以稍后在 CMake 脚本中使用。环境变量描述了package
导出的头文件在哪里、源文件在哪里、包依赖的库以及这些库的路径。名称始终遵循 <PACKAGE NAME>_<PROPERTY>
的约定:
<NAME>_FOUND
-:如果找到库,则设置为 true,否则设置为 false<NAME>_INCLUDE_DIRS
或 <NAME>_INCLUDES
:package
导出的include
路径<NAME>_LIBRARIES
或 <NAME>_LIBS
:package
导出的库<NAME>_DEFINITIONS
:?
查找编译所需的其他 CMake/Catkin 包:
find_package(<package-name
> REQUIRED COMPONENTS component1
commponent2
…)
例如:新建的软件包依赖roscpp、rospy、std_msgs
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
对于这个新建的catkin软件包,将它依赖的其他catkin packages
(roscpp、rospy、std_msgs)指定为catin的组件,这是最推荐的方法,这样操作会使它们的include路径
、libraries路径
等附件到catkin_variables
中。例如,catkin_INCLUDE_DIRS
不仅包含了catkin package
自己的include路径
,还包含组件(例如上面的roscpp、rospy、std_msgs)的include路径
,在后续的使用中会很方便!
include_directories(${catkin_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES})
既然有这么多好处,为什么不能把find_package(PythonLibs REQUIRED)整合到一起呢?
这是因为find_package()
一次只能查找一个package,catkin是指ROS工程中使用catkin工具编译的packages,而且凡是有catkin编译的包都可以作为catkin的组件,也可以作为单独的package,但PythonLibs
不行,因为它不是ros系统的package,属于第三方package。
2.4 catkin_package()
catkin_package()
是给依赖本package的package使用的。可以向其他package导出依赖,这些依赖可能包含头文件、库,或者本package依赖的其他package。
必须在使用add_library()或add_executable()声明任何目标之前调用此函数。该函数有 5 个可选参数:
- INCLUDE_DIRS - 导出的包的包含路径(即 cflags)
- LIBRARIES - 从项目中导出的库
- CATKIN_DEPENDS - 本项目依赖的其他catkin项目
- DEPENDS - 此项目所依赖的非 catkin CMake 项目
- CFG_EXTRAS - 附加配置选项
catkin_package()
是在/opt/ros/melodic/share/catkin/cmake/catkin_package.cmake
文件定义的。调用 catkin_package()
之后,会在/devel/share/<package-name>/cmake
目录下生成CMake config模式的查找文件:<package-name>Config.cmake
和<package-name>Config-version.cmake
。这样,其他软件包package使用`find_package(…)时就会加载配置文件了,从而获得依赖项。
然后我们在看一篇ROS Answers上的问答:What is the purpose of CATKIN_DEPENDS?
。
https://answers.ros.org/question/58498/what-is-the-purpose-of-catkin_depends/
DEPENDS
和CATKIN_DEPENDS
是来告诉catkin将你软件包的依赖项传递给使用find_package(…)查找你的软件包package的软件包package。
例如:假设你(调用)find_package(Boost REQUIRED)
,并且在你的已安装的头文件中(包含)#include <boost/function.hpp>
。为了让一个依赖软件包package(别的软件包package)构建和链接你的头文件,他们需要在他们的include路径中有 Boost 的include目录,并且他们需要链接到 Boost 的库。既然你已经在你头文件中使用了它,那么它们应该能从你那获得那个依赖。换言之,正因为它们是依靠你的软件包package构建的,所以它们不需要 find_package(Boost REQUIRED)
了,并且不另外使用Boost。
你的软件包package依赖于Boost这一事实是一个实现细节。因此当一些(软件包package)通过find_package(...)
查找你的软件包package时,它们能够间接获得对Boost的依赖。其工作方式是在catkin_package(…)
调用中放入DEPENDS Boost
。在内部,catkin将通过find_package(Boost)
获取路径,并且向 ${your_pkg_LIBRARIES}
添加${Boost_LIBRARIES}
,向${your_pkg_INCLUDE_DIRS}
添加${Boost_INCLUDE_DIRS}
。
我应该注意,catkin 将获取您给它的确切内容并尝试 通过find_package(...)
(查找)该软件包package,然后尝试使用该包的_LIBRARIES
和_INCLUDE_DIRS
变量。这个关于find_package(…)
布局的假设并不总是成立,因为CMake没有强制这样做。
例如:当find_package(…)
ing Python时:
find_package(PythonLibs REQUIRED)
结果为变量像是PYTHON_INCLUDE_PATH
,
find_package(OpenGL REQUIRED)
结果为OPENGL_INCLUDE_DIR
。
除了变量前缀的大小写外,这实际的前缀也是不同(PythonLibs -> PYTHON
),并且这后缀也是不标准的(PYTHON_INCLUDE_PATH
and OPENGL_INCLUDE_DIR
vs *_INCLUDE_DIRS
)。
在这种情况下,你需要将使用INCLUDE_DIRS
选项的include dirs变量和使用LIBRARIES
选项的库明确地传递给catkin_package(…)
。
虽然CATKIN_DEPENDS
选项和DEPENDS
选项非常相似,但是你必须把catkin packages只放在这个list
中。对你的 catkin 进行分类的好处是依赖于一个单独的选项,catkin能够进行额外的检查并警告你潜在的不正确的做法。
最后,一个简单的例子CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.3)
project(foo)
find_package(Boost REQUIRED
COMPONENTS
system
thread
)
find_package(PythonLibs REQUIRED)
find_package(OpenGL REQUIRED)
find_package(catkin REQUIRED
COMPONENTS
rosconsole
roscpp
)
include_directories(
include
${catkin_INCLUDE_DIRS}
${OPENGL_INCLUDE_DIR}
${PYTHON_INCLUDE_PATH}
)
catkin_package(
INCLUDE_DIRS include ${OPENGL_INCLUDE_DIR}
LIBRARIES foo ${OPENGL_LIBRARIES}
CATKIN_DEPENDS roscpp
DEPENDS Boost
)
..
在这个例子中,你可以看到我(调用了)find_package(Boost...)
,并在DEPENDS
部分传递了它,因为它产生了兼容的CMake变量。虽然我(调用了)find_package(PythonLibs...)
并在内部使用了它,但是因为它不在我暴露的任何头文件中,所有不需要一起传递。虽然我(调用了) find_package(OpenGL...)
,但是因为它产生了不兼容的CMake变量,所以我将它明确地传递给 INCLUDE_DIRS
和 LIBRARIES
。最后,我(调用了)find_package(catkin...rosconsole roscpp)
,并在内部使用,但是可能我只在我的.c*文件使用了rosconsole,因此我不需要传递它,所以在CATKIN_DEPENDS
变量中我只放了roscpp。
最后一个例子,如果一个package直接使用一个像Boost
这样的依赖,它们应该确保通过find_package(...)
显式地查找它,并且它们不能通过其他package隐式依赖于它。
发生这种情况的一个例子是,如果一个软件包packagefoo将Boost作为依赖项导出,还有一个依赖于foo的软件包packagebar,但也在内部使用Boost,那么bar将在没有显示依赖Boost的情况下也能正常编译。但是后来foo可能决定重构并删除它对 Boost 的依赖。此刻bar将无法编译,因为它不再具有通过foo对 Boost 的隐式依赖。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)