關於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   自身包內沒有自己的頭文件
  ${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原理

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章