当前位置:网站首页>[ROS](03)CMakeLists.txt详解
[ROS](03)CMakeLists.txt详解
2022-08-02 14:02:00 【CynalFly】
文章只是个人学习过程中学习笔记,主要参考ROS教程1。
1、概述
CMakeLists.txt是用于编译软件包的CMake编译系统的输入文件,几乎可以在任何文本编辑器中进行编辑。该文件描述了如何编译代码以及安装到何处。用于catkin项目的CMakeLists.txt文件是一个标准的附带一些额外约束的vanillaCMakeLists.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 文件解析
# 1 -- 项目所需要的CMake版本,向后兼容
cmake_minimum_required(VERSION 3.0.2)
# 2 -- 使用CMake project(package-name)声明软件包名称
# 可以通过使用变量 ${PROJECT_NAME} 在CMake脚本后面的任何位置引用项目名称。
project(beginner_tutorials)
# 3 -- CMake宏,查找编译所需的其他 CMake/Catkin 包(具体分析请看下面的2.3小节)
# [REQUIRED]:当没有满足条件的package时(例如指定的组件未找到等),
# 会终止CMake执行过程,并输出一条错误信息,反之,CMake会继续执行。
# [COMPONENTS]:要查找的组件(components),通常一个pakcage可能包含多个组件。
# roscpp、rospy...就是catkin的组件。
# 任何一个找不到就算失败,会终止CMake执行过程。
# 确保所有这些依赖package也在package.xml中,使用<depend>或<build_denpend>标记。
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
## 查找 PythonLibs 库
# find_package(PythonLibs REQUIRED)
## 查找C++ Boost库的组件库system、thread
# find_package(Boost REQUIRED COMPONENTS system thread)
## 4 -- 启用 Python 模块支持
## 如果这个package有setup.py,则取消对此的注释。
## 这个宏确保其中声明的模块和全局脚本得到安装
# catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
## 声明 ROS 消息、服务和动作 ##
################################################
## 5 -- 添加在`msg`文件夹中自定义的`*.msg`文件
## 将Message*.msg替换成你的.msg文件,
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
## 5 -- 添加在`srv`文件夹中自定义的`*.srv`文件
## 将Service*.msg替换成你的.srv文件
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
## 5 -- 添加在`action`文件夹中自定义的`*.action`文件
## 将Action*.action替换成你的.action文件
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )
## 6 -- 调用`消息`/`服务`/`动作`生成特定语言的接口文件
## 用于生成所有定义的message、service、action文件,需要添加本文件需要依赖的packages。
## 其实就是告诉编译器,编译 *.msg *.srv *.action 等文件时,需要依赖的库或package。
## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# std_msgs # Or other packages containing msgs
# )
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
###################################
## catkin specific configuration ##
###################################
## 7 -- catkin_package() (具体分析请看下面的2.3小节)
## 由catkin_package生成的文件最终会被安装到devel和build文件夹下。
## INCLUDE_DIRS: 如果package包含头文件,则取消对此行的注释
## LIBRARIES: 项目导出的库
## CATKIN_DEPENDS: 项目依赖的其余catkin项目
## DEPENDS: 项目依赖的其余非catkin项目
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES beginner_tutorials
# CATKIN_DEPENDS roscpp rospy std_msg
# DEPENDS system_lib
)
###########
## Build ##
###########
##
## 第一个参数“include”表示包中的include/目录也是路径的一部分。
## catkin_INCLUDE_DIRS:表示建立的环境变量
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
## 8 -- add_library() 生成库文件${PROJECT_NAME}
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/beginner_tutorials.cpp
# )
## 8 -- 定义target依赖的其他target
## add_dependencies(target-name depend-target1 depend-target2 ...)
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## 8 -- 生成可执行的二进制文件
## add_executable(<生成> <源文件>)
## 使用${PROJECT_NAME}引用变量,生成一个名为beginner_tutorials_node的可执行文件。
# add_executable(${PROJECT_NAME}_node src/beginner_tutorials_node.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## 8 -- 指定可执行目标链接的库
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
#############
## Install ##
#############
## 安装python可执行脚本
# catkin_install_python(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## 9 -- 安装规则 -- 目标文件的安装
## DESTINATION:定义了安装的路径
## 可执行二进制文件beginner_tutorials_node 安装到 ${CATKIN_PACKAGE_BIN_DESTINATION}目录
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## ARCHIVE:静态库
## LIBRARY:动态库
## RUNTIME:可执行目标二进制
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )
## 9 -- 安装规则 -- 目录的安装
## 如果目录以“/”结尾,表示将目录中的内容安装到目标路径
## 如果目录不以“/”结尾,表示将目录安装为目标路径下的目录
## PATTERN:用于使用正则表达式进行过滤
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )
## 9 -- 安装规则 -- 普通文件的安装
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
#############
## Testing ##
#############
## 添加基于gtest的cpp测试目标和链接库
# catkin_add_gtest(${PROJECT_NAME}-test test/test_beginner_tutorials.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
2.3 find_package()
如果 CMake 通过find_package()2 找到一个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 component1commponent2 …)
例如:新建的软件包依赖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()2 是给依赖本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 的隐式依赖。
ROS.org. ROS教程[EB/OL]. 2020-12-22[2022-7-5].
http://wiki.ros.org/cn/ROS/Tutorials. ︎ROS.org. CMakeLists.txt[EB/OL]. 2019-07-25[2022-07-05].
http://wiki.ros.org/catkin/CMakeLists.txt.︎︎
边栏推荐
- 网络剪枝(1)
- xshell连接虚拟机步骤_建立主机与vm虚拟机的网络连接
- 【Tensorflow】AttributeError: '_TfDeviceCaptureOp' object has no attribute '_set_device_from_string'
- About the development forecast of the market outlook?2021-05-23
- A number of embassies and consulates abroad have issued reminders about travel to China, personal and property safety
- logback源码阅读(二)日志打印,自定义appender,encoder,pattern,converter
- 期货具体是如何开户的?
- Interviewer: Can you talk about optimistic locking and pessimistic locking?
- Mysql's case the when you how to use
- 第三单元 视图层
猜你喜欢

The world's largest Apache open source foundation is how it works?

监管再次重拳出击,后市如何?2021-05-22

paddleocr window10初体验

Differences and concepts between software testing and hardware testing

shell脚本“画画”

Data Organization---Chapter 6 Diagram---Graph Traversal---Multiple Choice Questions

文件加密软件有哪些?保障你的文件安全

跑跑yolov5吧

ZABBIX配置邮件报警和微信报警

Sentinel源码(三)slot解析
随机推荐
Flask上下文,蓝图和Flask-RESTful
rust使用mysql插入数据
Some impressions of the 519 plummet 2021-05-21
Shell脚本完成pxe装机配置
MobileNet ShuffleNet & yolov5替换backbone
redis延时队列
泡利不相容原理适用的空间范围(系统)是多大?
yolov5,yolov4,yolov3乱七八糟的
监管再次重拳出击,后市如何?2021-05-22
跑跑yolov5吧
【Tensorflow】AttributeError: module 'keras.backend' has no attribute 'tf'
深度学习框架pytorch快速开发与实战chapter4
瑞吉外卖笔记——第05讲Redis入门
vim复制粘贴_vim如何复制粘贴
Audio processing: floating point data stream to PCM file
世界上最大的开源基金会 Apache 是如何运作的?
跑yolov5又出啥问题了(1)p,r,map全部为0
stack && queue
FFmpeg AVPacket详解
Flask框架