关于ROS包中CMakeList.txt中几个常用的命令的作用 ——————(一)

2024-06-22 16:38

本文主要是介绍关于ROS包中CMakeList.txt中几个常用的命令的作用 ——————(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

目录

(1)find_package(catkin REQUIRED  COMPONENTS  ...)

(2)include_directories() 

(3)add_executable(  src1 src2 ...)   (简易写法)

重点的命令总结:  

参考链接:


首先要知道,可以把CMakeLists.txt当成是一串源代码,CMakeLists.txt有自带的一些“系统变量”,里面的每个命令都是一个个“函数”,函数也需要按照要求填写正确格式的“变量”,我们可以通过这些“函数”对Cmake提出一些要求,或配置,进而对makefile进行一系列的自动的“书写”,最终使用make指令利用makefile对源代码进行编译。

其中这些“系统变量”有些是不显式写在CMakeLists.txt中的,比如程序包A的头文件路径用变量A_INCLUDE_DIRS表示,它就是一个“系统变量”,可以通过find_package这个“函数(命令)”去命令cmake工具去自动寻找程序包A的头文件路径,并当成“函数返回值”写到A_INCLUDE_DIRS这个变量中,这样就可以在后面的CMakeLists.txt中显式使用A_INCLUDE_DIRS这个变量了,这种“系统变量”通常使用${A_INCLUDE_DIRS}的形式来引用。

(1)find_package(catkin REQUIRED  COMPONENTS  ...)

(摘抄[1])find_package告诉cmake去按照优先级顺序在指定的搜索路径进行查找Findxxx.cmake文件和xxxConfig.cmake文件(其中xxx代表库的名字,特别注意的是有大小写之分),这两个文件大体上是没有区别的,cmake能够找到这两个文件中的任何一个,我们都能成功使用该库,也就是我们可以用库的内置好了Cmake变量。包含了库的头文件和库文件的路径信息,虽然库的作者一般会提供这两个文件,但是也会遇到安装完毕后找不到的情况。当我们在cmake..命令之后,Cmake 会读取执行CMakeLists.txt中的代码,当执行find_package()这条命令后,Cmake 就会从某些路径中找这Findxxx.cmake文件或者xxxConfig.cmake文件,Cmake找到任意一个之后就会执行这个文件,然后这个文件执行后就会设置好一些Cmake变量。Cmake比如下面的变量(NAME表示库的名字 比如可以用Opencv 代表Opencv库):

<NAME>_FOUND      //是否找到该库的标志位
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES   //该库的头文件地址
<NAME>_LIBRARIES  or  <NAME>_LIBS    //该库的静态或者动态链接文件地址
<NAME>_DEFINITIONS        //不懂这个

一般常用的就是xxx_FOUND 、xxx_INCLUDE_DIRS、xxx_LIBS,分别代表是否找到库的标志、库的头文件路径、库文件路径。find_package()有两种模式:Module模式和Config模式,分别对应上面的Findxxx.cmake 和xxxConfig.cmake两个文件。cmake默认优先Module模式,而Config模式是备选项。

Module模式(仅仅查找Findxxx.cmake文件):

Cmake会优先搜索CMAKE_MODULE_PATH指定的路径,如果在CMakeLists.txt中没有设置CMAKE_MODULE_PATH为存储Findxxx.cmake的路径,也就是说没有下面的指令:

set(CMAKE_MODULE_PATH "Findxxx.cmake文件所在的路径")

那么Cmake不会搜索CMAKE_MODULE_PATH指定的路径,此时Cmake会搜索第二优先级的路径,也就是<CMAKE_ROOT>/share/cmake-x.y/Mdodules (注意:x.y表示版本号。我的是3.10)。其中CMAKE_ROOT是你在安装Cmake的时候的系统路径,因为我并没有指定安装路径,所以是系统默认的路径,在我的系统中(ubuntu16.04)系统的默认路径是/usr/loacl,如果你在安装的过程中使用了

cmake -DCMAKE_INSTALL_PREFIX=自己dir路径 

那么此时CMAKE_ROOT就代表那个你写入的路径 。刚刚说道第一优先级的路径搜索没有找到Findxxx.cmake文件,就会到第二优先级的路径下搜索。如果Cmake在两个路径下都没有找到Findxxx.cmake文件。那么Cmake就会进入Config模式。

Config模式(仅仅查找xxxConfig.cmake文件):

Cmake会优先搜索xxx_DIR 指定的路径。如果在CMakeLists.txt中没有设置这个cmake变量。也就是说没有下面的指令:

set(xxx_DIR "xxxConfig.cmkae文件所在的路径")

那么Cmake就不会搜索xxx_DIR指定的路径,此时Cmake 就会自动到第二优先级的路径下搜索,也就是/usr/local/lib/cmake/xxx/中的xxxConfig.cmake文件。

上面主要讲了Cmake的搜索模式。如果Cmake在两种模式提供的路径中没有找到对应的Findxxx.cmake和xxxConfig.cmake文件,此时系统就会提示最上面的那些错误信息。

总结:

  • 首先简单来讲:find_package用来查找一些包的路径、头文件路径等信息,例如在A的CMakeList.txt中,设定find_package(B),该命令不会直接使得cmake知道工程A需要依赖B,而只是告诉cmake去查找B,然后返回一些相关变量而已。
  • 可以通过set命令手动设置所需要的库路径(其实是对应的Findxxx.cmake和xxxConfig.cmake文件路径),此功能很重要,第一:当系统处于某种原因找不到需要的库文件的时候,就需要手动指定。第二:当系统包含多个版本的某库时候,比如PCL1.7与PCL1.8共存的时候,可以通过set命令手动选择需要哪个库。

在ROS中的使用: 

find_package的标准命令格式是:

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE][REQUIRED] [[COMPONENTS] [components...]][OPTIONAL_COMPONENTS components...][NO_POLICY_SCOPE])

 在ROS中,在写CMakeList.txt的时候,至少需要指明去寻找“catkin”这个包,如果自己需要依赖于其他自己写的catkin_package,最好是把他们当成catkin这个包的一个COMPONENTS去寻找,比如:

看似很多catkin_package,实际上都是catkin这个包的组件!!!

把自己写的catkin_package当成catkin的COMPONENTS的好处是,这些自己的或者其他系统写好的catkin_package包,它们的include路径、libraries路径等自动添加到 catkin_ variables中,也就是说, catkin_INCLUDE_DIRS不仅包含catkin包自己的include路径,还包含了它的以上所有COMPONENTS(组件)的路径。同理catkin_LIBRARIES也包含了这些组件的库路径,这样就很方便了!

比如,catkin程序包A在调用catkin程序包B中的头文件 b.h ,只需要在包A的CMakeList.txt中,在find_package中,写上:

find_package(catkin REQUIRED  COMPONENTS  B)

并声明依赖关系,确保B先于A构建出来

add_dependencies(<A的target> <依赖的B的target>)

这样就可正常使用B的头文件了。通常情况下,因为b.h以及对应的b.cpp参与了A中某可执行文件的构建,那么在A的CMakeList.txt文件中使用include_directories(B_INCLUDE_DIRS)指明对b.h的引用,以及使用target_link_libraries(A_target B_LIBRARIES)指明对B库的链接。

但是当把B当做catkin的组件时,仅仅使用默认的命令即可,如下,而不用再手动添加

include_directories( ${catkin_INCLUDE_DIRS})
target_link_libraries(<A的target>  ${catkin_LIBRARIES})

 Note:所以,虽然上面两句不用再指明包B,但一定要在find_package 中指明,只有这样才能把B的变量添加到catkin_variables中,一起用!

这点更详细的解释建议参看官方解释http://wiki.ros.org/catkin/CMakeLists.txt

(2)include_directories() 

这个命令主要解决的问题是:如果在包A中的某 *.cpp 文件中调用了一个头文件,比如#include <opencv/cv.h>,gcc去哪里找这个头文件的问题;

我们可以使用#include “/usr/local/include/opencv/cv.h”来直接显式的给出头文件地址,但是显然不规范,我们可以通过在A的CMakeList.txt中给出关于它的一个根目录,即命令:

include_directories(/usr/local/include)

使得系统知道以/usr/local/include为基础去寻找头文件opencv/cv.h。

此外,结合find_package()命令,可以很方便的自动填写关于需要的头文件的路径,比如对于opencv,使用以下命令就可以自动查找头文件路径

find_package(opencv REQUIRED) 
include_directories(${OPENCV_INCLUDE_DIRS})

注意点:

  • 包A自身的头文件夹include也需要被填写进来(如果有的话),并且要提前于其他包的路径。
  • 对于catkin类型自带的程序包,在find_package中指明之后,在include_directories只需要对应的写上${catkin_INCLUDE_DIRS}就可以指代所有的:
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
include_directories(#下面的include指的就是自身的头文件文件夹include   #下面的是find_package指令返回的头文件路径${catkin_INCLUDE_DIRS}
)

(3)add_executable(<name>  src1 src2 ...)   (简易写法)

使用add_executable指令告诉gcc,要生成名为<name>的可执行文件,这个可执行文件由src1、src2...来编译生成,注意,所有牵涉到<name>编译的文件都要列出来。

比如,对于包A中src文件夹里面有文件test.cpp  math.cpp  matrix.cpp三个,include文件夹里面有math.h   matrix.h  两个头文件,在test.cpp (有 main函数)中分别用

#include"math.h"
#include"matrix.h"

调用了math.cpp  matrix.cpp,那么在生成<name>为test的可执行文件的时候,要填写

add_executable(test   src/test.cpp  src/math.cpp  src/matrix.cpp )

因为,在生成test可执行文件的过程中,需要把src/test.cpp  src/math.cpp  src/matrix.cpp三个文件的obj文件进行相互链接。 

该命令要放在add_executable命令之后,当add_executable执行完之后,会生成名称为name的二进制文件,此时还没有完全生成可执行文件,比如name的源代码在头文件调用了boost库,那么在生成name的最终可执行文件时候,还需要去和boost的库文件进行链接,最终才可以生成最终的可执行文件。

add_executable的作用就是告诉cmake,把刚刚生成的name,与该包依赖的其他外部库文件( lib1  lib2 ...)等进行链接,生成最终的可执行文件。截个图看一下输出

重点的命令总结:  

这几个命令按照顺序填写,可以完成基本的配置,可以看出,CMakeList.txt将一个编译过程,分为了几个阶段,基本上就是按照编译的步骤来的。

步骤1.查找依赖包(库和头文件)的路径

 

find_package()

步骤2.为自己源代码的头文件提供路径

 

include_directories()

步骤3.编译自己的源代码为目标文件

 

add_executable()

步骤4.把自己的目标文件与其他库文件进行链接

 

target_link_libraries()

因此,在cmake报错的时候,可按照错误分类,判断是找不到包(find_package可能有问题),还是没有找到头文件(include_directories),还是链接失败啥的,一一分析去找,有可能问题是比较综合的。

下一篇是其他相关的配置命令,有些不是必要的,但是却十分常用,但是也是十分重要的。传送门:https://blog.csdn.net/u012057432/article/details/103353547

参考链接:

[1] https://blog.csdn.net/chengde6896383/article/details/86497016  find_package原理

 

 

这篇关于关于ROS包中CMakeList.txt中几个常用的命令的作用 ——————(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084838

相关文章

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

React+TS前台项目实战(十七)-- 全局常用组件Dropdown封装

文章目录 前言Dropdown组件1. 功能分析2. 代码+详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲全局Dropdown组件封装,可根据UI设计师要求自定义修改。 Dropdown组件 1. 功能分析 (1)通过position属性,可以控制下拉选项的位置 (2)通过传入width属性, 可以自定义下拉选项的宽度 (3)通过传入classN

Linux 下的Vim命令宝贝

vim 命令详解(转自:https://www.cnblogs.com/usergaojie/p/4583796.html) vi: Visual Interface 可视化接口 vim: VI iMproved VI增强版 全屏编辑器,模式化编辑器 vim模式: 编辑模式(命令模式)输入模式末行模式 模式转换: 编辑-->输入: i: 在当前光标所在字符的前面,转为输入模式

Java面试八股之JVM参数-XX:+UseCompressedOops的作用

JVM参数-XX:+UseCompressedOops的作用 JVM参数-XX:+UseCompressedOops的作用是启用对象指针压缩(Ordinary Object Pointers compression)。这一特性主要应用于64位的Java虚拟机中,目的是为了减少内存使用。在传统的64位系统中,对象引用(即指针)通常占用8字节(64位),而大部分应用程序实际上并不需要如此大的地址空间

帆软报表常用操作

欢迎来到我的博客,代码的世界里,每一行都是一个故事 🎏:你只管努力,剩下的交给时间 🏠 :小破站 帆软报表常用操作 多序号实现使用数据集作为参数空白页或者竖线页修改页面Title金额,或者保留两位小数等等设置日期格式显示图片使用公式 多序号实现 所用函数为SEQ(),如果一张报表中需要用到多个序号,那么就需要加入参数SEQ(1),SEQ(

常用MQ消息中间件Kafka、ZeroMQ和RabbitMQ对比及RabbitMQ详解

1、概述   在现代的分布式系统和实时数据处理领域,消息中间件扮演着关键的角色,用于解决应用程序之间的通信和数据传递的挑战。在众多的消息中间件解决方案中,Kafka、ZeroMQ和RabbitMQ 是备受关注和广泛应用的代表性系统。它们各自具有独特的特点和优势,适用于不同的应用场景和需求。   Kafka 是一个高性能、可扩展的分布式消息队列系统,被设计用于处理大规模的数据流和实时数据传输。它

Linux中拷贝 cp命令中拷贝所有的写法详解

This text from: http://www.jb51.net/article/101641.htm 一、预备  cp就是拷贝,最简单的使用方式就是: cp oldfile newfile 但这样只能拷贝文件,不能拷贝目录,所以通常用: cp -r old/ new/ 那就会把old目录整个拷贝到new目录下。注意,不是把old目录里面的文件拷贝到new目录,

XMG 常用的手势

// 创建点按手势     UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];    tap.numberOfTabsRequired=2; //点击的次数

Avalonia 常用控件二 Menu相关

1、Menu 添加代码如下 <Button HorizontalAlignment="Center" Content="Menu/菜单"><Button.Flyout><MenuFlyout><MenuItem Header="打开"/><MenuItem Header="-"/><MenuItem Header="关闭"/></MenuFlyout></Button.Flyout></B