关于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 COMPONENTS
  roscpp
  rospy
  std_msgs
  message_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文件进行相互链接。 

(4)target_link_libraries(<name> lib1  lib2 ...) (简易写法,lib是依赖库的路径)

该命令要放在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原理

 

猜你喜欢

转载自blog.csdn.net/u012057432/article/details/103353148