1. 程式人生 > >ROS學習筆記15(ROS/CMakeLists.txt檔案)

ROS學習筆記15(ROS/CMakeLists.txt檔案)

1 概述

CMakeLists.txt檔案是構建軟體包所必備的檔案 ,其描述瞭如何構建程式以及在哪裡安裝程式包。任何一個檔案包通常都會包含一個或者多個CMakeLists.txt檔案。CMakeLists.txt檔案遵守了vanilla 標準,用於一個catkin專案,含有一定的約束條件。

2 整體結構和結構

CMakeListx.txt檔案必須準守下面的這些守則,否則的話,程式包將不能被正確的構建。在配置檔案中,下面的順序很重要。

  • Required CMake Version (cmake_minimum_required)

  • Package Name (project())

  • Find other CMake/Catkin packages needed for build (find_package())

  • Enable Python module support (catkin_python_setup())

  • Message/Service/Action Generators (add_message_files(), add_service_files(), add_action_files())

  • Invoke message/service/action generation (generate_messages())

  • Specify package build info export

    (catkin_package())

  • Libraries/Executables to build (add_library()/add_executable()/target_link_libraries())

  • Tests to build (catkin_add_gtest())

  • Install rules (install())

3 CMake的版本

每一個CMakeLists.txt檔案都是以所需要的CMake的版本開始。Catkin需要的CMake版本至少2.8.3。

cmake_minimum_required(VERSION 2.8.3)

4 軟體包的名字

然後的條款是由CMake 專案函式指定包的名稱。比如,我們構建了一個叫做robot_brain的安裝包。

project(robot_brain)

需要注意的是,在之後的CMake指令碼中,你可以使用${PROJECT_NAME}引用專案名稱。

5 找到依賴的CMake包

我們使用CMake find_package函式來指定一些必備的用來構建我們專案的CMake軟體包。通常存在有至少一個被依賴的軟體包:catkin。

find_package(catkin REQUIRED)

如果你的專案也依賴於其他的一些軟體包,可以將他們自動的被轉化為catkin的元件;並不是直接使用find_package於這些軟體包,而是將它們轉化為元件,這會令任務更簡單。例如,假如你想使用nodelet軟體包。

find_package(catkin REQUIRED COMPONENTS nodelet)

注意:find_package 的元件功能僅用於當你想要找到構建專案的軟體包,不要新增 專案runtime時的依賴。

你也可以這樣做:

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

當然,這是一種很不方便的做法。

5.1  find_package()做了什麼

如果一個軟體包被CMake通過find_package函式找到,那麼系統會自動的生成一系列描述軟體包的CMake 環境變數。這些環境變數會稍後被CMake指令碼使用。與此同時,這些環境變數描述了軟體包會把標頭檔案匯出到了那裡,以及哪些被軟體包依賴的庫和這些庫的路徑。環境變數的命名通常是:<PACKAGE NAME>_<PROPERTY>:

<NAME>_FOUND 當庫被找到的時候,被設定為true;否則,設定為false;

<NAME>_INCLUDE_DIRS 或者 <NAME>_INCLUDES 包含了軟體包匯出的路徑

<NAME>_LIBRARIES 或者 <NAME>_LIBS 被軟體包匯出的庫

<NAME>_DEFINITIONS ?

5.2 Catkin Packages 為什麼要被指定為元件?

Catkin軟體包並不是catkin真正的元件。不過設計出來的CMake的元件功能卻可以明顯的節約你的輸入時間。

對於catkin 軟體包,當使用find_package找到它們並把它們當做catkin元件,則這種方法的優勢很明顯,而且其建立了一系列有著catkin_字首的環境變數。舉個例子,當想要在程式中使用nodelet軟體包的時候,一種比較建議的方法如下:

find_package(catkin REQUIRED COMPONENTS nodelet)

這意味著包含路徑,庫,等等被nodelet軟體包匯出的變數也被新增到了catkin_variables。比如,catkin_INCLUDE_DIRS不僅僅包含了catkin的路徑同時也包含了nodelet的路徑。這在之後用很有用。

當然我們也可以使用下面的這種方法:

find_package(nodelet)

然而這意味著nodelet的路徑,庫等等並不會新增到catkin_variables中。

這會產生了nodelet_INCLUDE_DIRS,nodelet_LIBRARIES等等一系列的變數。

同樣的變數也可以使用下面方法建立:

find_package(catkin REQUIRED COMPONENTS nodelet)

5.3 Boost

如果使用C++和Boost,你需要援引find_package()於Boost並且需要特別指定Boost的那些方面。例如,當你想要使用Boost threads時,你要如下這樣做:

find_package(Boost REQUIRED COMPONENTS thread)

6 catkin_package()

catkin_package()是一個catkin_provided 的CMake macro檔案。構建系統必須要指定catkin特定的資訊,而系統又用於生成類pkg-config和CMake檔案。

在使用add_library()和add_executable()函式宣告目標之前,必須的呼叫這個函式。這個函式有5個可選的引數。

INCLUDE_DIRS - 包含了匯出軟體包標頭檔案的路徑

LIBRARIES - 專案匯出的庫

CATKIN_DEPENDS - 這個專案依賴的其他catkin專案

DEPENDS - 這個專案依賴的其他non-catkin專案。更詳細的講解,參考this explanation.

CFG_EXTRAS - 其他額外的一些配置選擇

比較詳細的macros 說明檔案可以參考 here.

舉個例子:

catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

其中inlcude 中是軟體包匯出的標頭檔案路徑。CMake環境變數${PROJECT_NAME}就是之前傳遞給project()函式的引數,在這個例子中,它就是“robot_brain”。"roscpp" + "nodelet"是需要構建這個軟體包所必須的catkin軟體包,而"eigen" + "opencv"是構建這個軟體包的系統依賴,也就是非non-catkin軟體包。

7 指定生成目標

有很多的方式可以生成目標檔案。但通常具有代表性的是下面。

1.可執行目標。我們可以執行的程式。

2.庫目標。被可執行目標在構建或者執行時間時使用的庫。

7.1 目標命名

非常重要的是需要注意,catkin中構建目標的名稱必須是唯一的,不管它們是構建/安裝到哪個資料夾。這是CMake的要求。但是,目標的名稱僅在CMake內部是必需的。可以使用set_target_properties()函式將目標重新命名為其他目標:

例:

set_target_properties(rviz_image_view
                      PROPERTIES OUTPUT_NAME image_view
                      PREFIX "")

這將在構建和安裝輸出中將目標rviz_image_view的名稱更改為image_view

7.2 自定義輸出目錄

雖然可執行檔案和庫的預設輸出目錄通常被設定為合理的值,但在某些情況下必須自定義。比如,包含Python繫結的庫必須放在不同的資料夾中,以便可以在Python中匯入,如下:

例:

set_target_properties(python_module_library
  PROPERTIES LIBRARY_OUTPUT_DIRECTORY $ {CATKIN_DEVEL_PREFIX} / $ {CATKIN_PACKAGE_PYTHON_DESTINATION})

7.3 包含路徑和庫路徑

在指定目標之前,需要指定可以構建目標的資源位置,特別是標頭檔案和庫:

  • 包含路徑 - 在哪裡可以找到正在構建的程式碼(在C / C ++中最常見)的標頭檔案
  • 庫路徑 - 可執行目標構建的庫位於何處?
  • include_directories(<dir1>,<dir2>,...,<dirN>)

  • link_directories(<dir1>,<dir2>,...,<dirN>)

7.3.1 include_directories()

include_directories的引數應該是find_package呼叫二生成的* _INCLUDE_DIRS變數以及其他需要包含的任何其他目錄。如果您使用catkin和Boost,則include_directories()呼叫應如下所示:

include_directories(include $ {Boost_INCLUDE_DIRS} $ {catkin_INCLUDE_DIRS})

第一個引數“include”表示包中的include 目錄也是路徑的一部分(其中包含了標頭檔案的路徑)。

7.3.2 link_directories()

CMake link_directories()函式可用於新增其他庫路徑,但並不建議這樣做。所有catkin和CMake軟體包在find_packaged時自動新增其連結資訊。只需連結到target_link_libraries()中的庫即可。

例:

link_directories(〜/ my_libs)

請參閱本cmake的教程可以看到使用的詳細例子使用target_link_libraries()以及link_directories() 。

7.4 可執行目標

要指定必須構建的可執行目標,我們使用add_executable()這個CMake函式。

例:

add_executable(myProgram src / main.cpp src / some_file.cpp src / another_file.cpp)

這將構建一個名為myProgram的目標可執行檔案,它由3個原始檔構成:src/main.cpp,src/some_file.cpp和src/ another_file.cpp。

7.5 庫目標

add_library()這個 CMake函式用來指定庫來構建。預設情況下,catkin會構建共享庫。

例:

add_library($ {PROJECT_NAME} $ {$ {PROJECT_NAME} _SRCS})

7.6 target_link_libraries

使用target_link_libraries() 函式可以指定可執行目標連結的庫。

這通常在add_executable()呼叫之後完成。如果ROS 找不到的話,則應該新增$ {catkin_LIBRARIES}。

句法:

target_link_libraries(<executableTargetName>,<lib1>,<lib2>,... <libN>)

例:

add_executable(foo src / foo.cpp)
add_library(moo src / moo.cpp)
target_link_libraries(foo moo) - This links foo against libmoo.so

注意,在大多數用例中不需要使用link_directories(),因為該資訊是通過find_package()自動引入的。

8 訊息,服務和操作目標

ROS中的messages(.msg),services(.srv)和action(.action)檔案在構建和使用ROS包之前,需要進行特殊的前處理器構建步驟。這些巨集是生成指定程式語言的檔案的關鍵,以便可以使用所選程式語言中的訊息,服務和操作。構建系統可以使用所有可用的生成(例如gencpp,genpy,genlisp等)。

提供了三個巨集來分別處理訊息,服務和操作:

  • add_message_files

  • add_service_files

  • add_action_files

必須在呼叫生成的巨集之後呼叫這些巨集:

 generate_messages()

8.1 重要的先決條件和限制

  • 這些macros必須在catkin_package()函式使用之前呼叫,以便生成正常工作。

 find_package(catkin REQUIRED COMPONENTS ...)
 add_message_files(...)
 add_service_files(...)
 add_action_files(...)
 generate_messages(...)
 catkin_package(...)
 ...
  • catkin_package()函式必須包含這種CATKIN_DEPENDS對message_runtime依賴關係。

catkin_package(
 ...
 CATKIN_DEPENDS message_runtime ...
 ...)
  • find_package()必須包含有包messagese_generation,可以單獨使用,也可以作為catkin的一個元件使用:

find_package(catkin REQUIRED COMPONENTS message_generation)
  • package.xml檔案構建時的依賴必須包含:message_generation;執行時的依賴必須包含:message_runtime。如果依賴性是從其他包傳遞的,那麼這不是必需的。

  • 如果您有一個目標(甚至傳遞)依賴一些其他需要構建訊息/服務/操作的目標,則需要在目標catkin_EXPORTED_TARGETS上新增顯式依賴項,以便它們以正確的順序構建。除非您的軟體包確實不使用ROS的任何部分,否則這種情況幾乎總是適用。不幸的是,這種依賴關係不能自動傳播。( some_target 是被add_executable()構建的目標名稱):

  add_dependencies(some_target $ {catkin_EXPORTED_TARGETS})
  • 如果您有一個構建訊息和/或服務的包以及使用這個包的可執行檔案,則需要在自動生成的訊息目標上建立顯式依賴項,以便以正確的順序構建它們。( some_target 是被add_executable()構建的目標名稱)

  add_dependencies(some_target $ {$ {PROJECT_NAME} _EXPORTED_TARGETS})
  • 如果您的包同時滿足上述兩個條件,則需要新增兩個依賴項,即:

  add_dependencies(some_target $ {$ {PROJECT_NAME} _EXPORTED_TARGETS} $ {catkin_EXPORTED_TARGETS})

8.2 例子

軟體包在名為“msg”的目錄下分別有兩個訊息檔案:“ MyMessage1.msg ”和“ MyMessage2.msg ” ,這些訊息依賴於std_msgssensor_msgs。軟體包在名為“srv”的目錄中有一個服務檔案:“ MyService.srv ”。使用這些訊息和服務的可執行檔案為message_program。只使用ROS部分的功能和不使用這個軟體包中定義的訊息/服務一個可執行檔案do_not_use_local_messages_program,那麼您需要在CMakeLists.txt中使用以下內容:

# Get the information about this package's buildtime dependencies
  find_package(catkin REQUIRED
    COMPONENTS message_generation std_msgs sensor_msgs)

  # Declare the message files to be built
  add_message_files(FILES
    MyMessage1.msg
    MyMessage2.msg
  )

  # Declare the service files to be built
  add_service_files(FILES
    MyService.srv
  )

  # Actually generate the language-specific message and service files
  generate_messages(DEPENDENCIES std_msgs sensor_msgs)

  # Declare that this catkin package's runtime dependencies
  catkin_package(
   CATKIN_DEPENDS message_runtime std_msgs sensor_msgs
  )

  # define executable using MyMessage1 etc.
  add_executable(message_program src/main.cpp)
  add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

  # define executable not using any messages/services provided by this package
  add_executable(does_not_use_local_messages_program src/main.cpp)
  add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})

另外,如果要構建actionlib操作,且在“action”目錄中有一個名為“ MyAction.action ” 的操作規範檔案,則必須將actionlib_msgs新增到使用catkin find_packaged的元件列表中,並在之前新增以下呼叫對generate_messages(...)的呼叫:

add_action_files(FILES
  MyAction.action
)

包必須具有對actionlib_msgs的構建(build)依賴性。

9  啟用Python模組支援

如果你的ROS包提供了一些Python模組,你應該建立一個setup.py檔案並呼叫

catkin_python_setup()

使用上述函式需要在呼叫generate_messages()和catkin_package()之前。

10 單元測試

有一個特定於catkin的巨集用於處理名為catkin_add_gtest()的基於gtest的單元測試。

if(CATKIN_ENABLE_TESTING)
  catkin_add_gtest(myUnitTest test / utest.cpp)
endif()

11 可選步驟:指定可安裝目標

經過構建之後,需要將目標放置到catkin工作空間的開發空間中。但是,我們通常希望在系統中安裝目標(有關安裝路徑的資訊可以在REP 122中找到 ),以便其他人或本地資料夾可以使用它們來測試系統級安裝。換句話說,如果您希望能夠對程式碼進行“make install”,則需要指定目標應該結束的位置。

這是由CMake install()函式完成的,該函式有下面的幾個引數:

  • TARGETS - 那個被安裝的目標

  • ARCHIVE DESTINATION - 靜態庫和DLL(Windows).lib存根

  • LIBRARY DESTINATION - 非DLL共享庫和模組

  • RUNTIME DESTINATION - 可執行目標和DLL(Windows)樣式共享庫

舉個例子:

install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

除了這些標準目標,有時一些檔案必須安裝到特殊資料夾。即,必須將包含Python繫結的庫安裝到可在Python中匯入的其他資料夾中: 

install(TARGETS python_module_library
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
)

11.1 安裝Python的可執行指令碼

對於Python程式碼,安裝規則看起來不同,這是因為沒有使用add_library()和add_executable()函式,以便CMake確定哪些檔案是目標以及它們是什麼型別的目標。

在CMakeLists.txt檔案中使用以下內容:

catkin_install_python(PROGRAMS scripts/myscript
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})

有關安裝python指令碼和模組的詳細資訊,以及資料夾佈局的最佳實踐,請參閱catkin手冊

如果只安裝Python指令碼並且不提供任何模組,則既不需要建立上面提到的setup.py檔案(需要使用的時候,再進行查閱),也不需要呼叫catkin_python_setup()。

11.2 安裝標頭檔案

標頭檔案也必須安裝到“include”資料夾中,這通常通過安裝整個檔案完成(可以選擇使用檔名模式排除無需安裝的子檔案,比如排除SVM字尾的子檔案)。可以通過如下的安裝規則完成:

install(DIRECTORY include/${PROJECT_NAME}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

或者當子資料夾的名稱與包的名稱相沖突的時候

install(DIRECTORY include/
  DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

11.3 安裝roslaunch檔案和其他的一些資源

其他的一些資源,比如launchfiles可以被安裝這個檔案下:${CATKIN_PACKAGE_SHARE_DESTINATION}:

install(DIRECTORY launch/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
  PATTERN ".svn" EXCLUDE)