一、 概述
CMake構(gòu)建系統(tǒng)通過ROS包中的CMakeList.txt來構(gòu)建軟件包?;ハ嘁蕾嚨陌及粋€或者多個CMakeList.txt來描述如何編譯代碼和如何安裝。在catkin 項(xiàng)目中,CMakeList.txt 符合標(biāo)準(zhǔn)的vanilla CMakeList.txt 格式,但稍微有點(diǎn)不同。
二、 整體結(jié)構(gòu)和命令一覽
在編寫CMakeLists.txt時必須遵循特定的格式,否則軟件包將無法正確構(gòu)建和編譯。一個完整的ROS下CMakeList.txt由下面內(nèi)容組成
- 所需CMake版本(cmake_minimum_required)
- 軟件包名稱(project())
- 查找構(gòu)建所需的其他CMake / Catkin軟件包(find_package())
- 啟用Python模塊支持(catkin_python_setup())
- 消息/服務(wù)/動作生成器(add_message_files(),add_service_files(),add_action_files())
- 生成消息/服務(wù)/動作等自定義消息(generate_messages())
- 指定包的構(gòu)建信息輸出(catkin_package())
- 要建立的庫/可執(zhí)行文件(add_library()/ add_executable()/ target_link_libraries())
- 測試(catkin_add_gtest())
- 安裝規(guī)則(install())
三、 CMake版本
CMakeLists.txt文件必須以CMake的版本開頭。下面表示的是Catkin需要2.8.3或更高的版本。
cmake_minimum_required(VERSION 2.8.3)
四、 軟件包名稱
繼而需要指定CMake工程名,一般取作包名。例如我們的項(xiàng)目名叫做robot_brain
project(robot_brain)
而在CMake中,可以在CMake文本中使用變量${PROJECT_NAME}來引用項(xiàng)目名稱。
五、 查找相關(guān)的CMake包
接下來我們要用find_package指令來指定在構(gòu)建項(xiàng)目過程中依賴了哪些其他的包,在ROS中,catkin必備依賴,所以雷打不打地我們寫上catkin REQUIRED:
find_package(catkin REQUIRED)
在此基礎(chǔ)上,如果我們還需要依賴其他包(或者組件),我們就在 上述的包后面繼續(xù)添加包(組件)名即可:
find_package(catkin REQUIRED COMPONENTS nodelet)
或者可以寫成
find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)
值得注意一點(diǎn)的是:這里添加的包用于構(gòu)建包使用,而不是添加運(yùn)行時依賴項(xiàng)。
你也可以做:
find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)
如何寫,看個人習(xí)慣吧,如果不考慮篇幅,其實(shí)我更喜歡把所有包(組件)列成一列,直觀。在普通的CMakeLists.txt中是沒有find_package()的,那么,catkin中的這個語句有啥作用呢?
5.1 那find_package()作何用?
如果CMake通過find_package找到一個包,則會自動生成有關(guān)包所在路徑的CMake環(huán)境變量,環(huán)境變量描述了包中頭文件的位置,源文件的位置,包所依賴的庫以及這些庫的路徑。
這些變量名稱以< PACKAGE NAME >_< PROPERTY >的形式出現(xiàn):
- < NAME >_FOUND - 如果找到庫,則設(shè)置為true,否則為false
- < NAME > _INCLUDE_DIRS或<NAME> _INCLUDES - 包導(dǎo)出的包含路徑
- < NAME > _LIBRARIES或<NAME> _LIBS - 由包導(dǎo)出的庫
- < NAME > _DEFINITIONS - ?
- ...
5.2為啥Catkin包是組件形式?
實(shí)際上,Catkin的包并不是catkin的真正組成部分。而catkin采用CMake的組件功能,主要是為了節(jié)省打字時間。
將Catkin的包作為一種組件看待是很有好處的,當(dāng)你找到包的時候,各種以catkin為前綴的添加到CMakeLists.txt中了。以前述nodelet包為例:
find_package(catkin REQUIRED COMPONENTS nodelet)
catkin執(zhí)行到這個語句時,就會導(dǎo)出的nodelet的include路徑,庫等也添加到catkin前綴的變量中。例如,catkin_INCLUDE_DIRS不僅包含catkin的include路徑,還包含了nodelet,這在后面會用到。
當(dāng)然,我們可以只選擇找nodelet的:
find_package(nodelet)
很可惜這樣的做法會令nodelet路徑,庫等不會被添加到catkin變量中,從而生成了例如nodelet_INCLUDE_DIRS,nodelet_LIBRARIES這樣的變量等。
5.3Boost庫
如果使用C ++和Boost,則需要用find_package()來找Boost庫,并指定Boost中的組件。 舉個栗子,如果想使用Boost線程,就可以寫成:
find_package(Boost REQUIRED COMPONENTS thread)
六、catkin_package()
catkin_package()是一個catkin提供的CMake宏,用于將catkin特定的信息信息輸出到構(gòu)建系統(tǒng)上,用于生成pkg配置文件以及CMake文件。
這個命令必須在add_library()或者add_executable()之前調(diào)用,該函數(shù)有5個可選參數(shù):
- INCLUDE_DIRS - 導(dǎo)出包的include路徑
- LIBRARIES - 導(dǎo)出項(xiàng)目中的庫
- CATKIN_DEPENDS - 該項(xiàng)目依賴的其他catkin項(xiàng)目
- DEPENDS - 該項(xiàng)目所依賴的非catkin CMake項(xiàng)目。
- CFG_EXTRAS - 其他配置選項(xiàng)
舉個栗子:
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv)
這表示包文件夾中的文件夾“include”是導(dǎo)出頭文件的地方。 ${PROJECT_NAME}根據(jù)project中的內(nèi)容生成,此處是rrobot_brain。“roscpp”+“nodelet”是用來構(gòu)建/運(yùn)行此程序包的catkin包,而“eigen”+“opencv”是用于構(gòu)建/運(yùn)行此程序包的非catkin包。
七、指定構(gòu)建目標(biāo)
構(gòu)建目標(biāo)可以有多種形式,但通常主要有以下兩種:
- 執(zhí)行文件目標(biāo) - 可以運(yùn)行的程序
- 庫目標(biāo) - 可在構(gòu)建和/或運(yùn)行時給可執(zhí)行目標(biāo)使用的庫
7.1目標(biāo)命名
值得重點(diǎn)關(guān)注的是,catkin中的構(gòu)建目標(biāo)的名稱必須是唯一的,但是,但構(gòu)建目標(biāo)命名的唯一性只能在CMake內(nèi)部進(jìn)行??梢允褂?strong>set_target_properties()函數(shù)將目標(biāo)重命名為其他目標(biāo):
再舉個栗子:
set_target_properties(rviz_image_view
PROPERTIES OUTPUT_NAME image_view
PREFIX "")
這時就將rviz_image_view的名稱更改為image_view了。
7.2自定義輸出目錄
一般情況下,令輸出目標(biāo)目錄保持默認(rèn)就可以了,但有時遇到一些特定情況就得因地制宜。再再舉個栗子,一個包含了Python bindings的庫必須防止在不同的文件夾,才能夠被導(dǎo)入Python中,因此可以通過下述方式進(jìn)行:
set_target_properties(python_module_library
PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
7.3包含路徑和庫路徑
在生成目標(biāo)之前,請記得先寫好包含路徑和庫路徑,不然編譯會失敗。
- 包括路徑 - 頭文件
- 庫路徑 - 構(gòu)建對象需要用到的庫
include_directories(<dir1>,<dir2>,...,<dirN>)
link_directories(<dir1>,<dir2>,...,<dirN>)
7.3.1 include_directories()
include_directories()中的參數(shù)應(yīng)該是調(diào)用find_package調(diào)用時生成的* _INCLUDE_DIRS變量。 如果使用catkin和Boost,那么include_directories()調(diào)用應(yīng)該如下所示:
include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
第一個參數(shù)“include”表示包中的include /目錄也是路徑的一部分。
7.3.2 link_directories()
link_directories()函數(shù)可用于添加額外的庫路徑,但通常不推薦這樣做,因?yàn)?所有catkin和CMake軟件包在find_packaged時都會自動添加鏈接信息。 只需寫target_link_libraries()中就可以了。但真要寫,就按照下面那樣來寫:
link_directories(~/my_libs)
7.4 可執(zhí)行目標(biāo)
要指定必須構(gòu)建的可執(zhí)行目標(biāo),我們必須使用add_executable()CMake函數(shù)。
add_executable(myProgram
src/main.cpp
src/some_file.cpp)
構(gòu)建myProgram的目標(biāo)可執(zhí)行文件,3個源文件構(gòu)建:src / main.cpp,src / some_file.cpp和src / another_file.cpp。
7.5 庫目標(biāo)
add_library() 用于指定要構(gòu)建的庫。
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
7.6 target_link_libraries()
target_link_libraries()來指定可執(zhí)行目標(biāo)鏈接的庫。 通常在add_executable()調(diào)用之后完成。 如果找不到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
請注意,在大多數(shù)情況中不需要使用link_directories(),因?yàn)閒ind_package()自動拉入。
八、消息、服務(wù)和響應(yīng)
消息(.msg),服務(wù)(.srv)和響應(yīng)(.action)文件在ROS包構(gòu)建和使用之前需要一個特殊的預(yù)處理器構(gòu)建步驟。 這些宏的要點(diǎn)是生成編程語言特定的文件,以便可以利用其選擇的編程語言中的消息,服務(wù)和動作。 構(gòu)建系統(tǒng)將使用生成器(例如gencpp,genpy,genlisp等)生成綁定。
提供了三個宏來分別處理消息,服務(wù)和響應(yīng):
- add_message_files
- add_service_files
- add_action_files
而在之后必須在下面寫下面宏,catkin時候會在devel的include文件夾中生成對應(yīng)的頭文件:
generate_messages()
8.1 使用條件
為了能夠正常生成相應(yīng)的文件,這些宏必須在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()寫上message_generation組件:
find_package(catkin REQUIRED COMPONENTS message_generation)
package.xml文件中加上
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
如果構(gòu)建的對象依賴于其他需要構(gòu)建消息/服務(wù)/響應(yīng)的構(gòu)建對象,則需要向目標(biāo)catkin_EXPORTED_TARGETS添加明確的依賴關(guān)系,以便它們以正確的順序構(gòu)建。 這種情況比較常用,除非您的包真的不使用ROS的任何部分。 但這種依賴關(guān)系不能自動傳遞。 (some_target是由add_executable()設(shè)置的目標(biāo)的名稱):
add_dependencies(some_target${catkin_EXPORTED_TARGETS})
如果需要構(gòu)建消息或服務(wù)的包以及使用這些消息和/或服務(wù)的可執(zhí)行文件,則需要為自動生成的消息目標(biāo)創(chuàng)建明確的依賴關(guān)系,以便以正確的順序構(gòu)建它們。 (some_target是由add_executable()設(shè)置的目標(biāo)的名稱):
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
如果您的catkin包滿足上述兩個條件,則需要添加以下兩個依賴關(guān)系,即:
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
8.2 例子
現(xiàn)在有兩個依賴于std_msgs和sensor_msgs的消息MyMessage1.msg和MyMessage2.msg,還有一個自定義服務(wù)MyService.srv, message_program是使用這些消息和服務(wù)的指令,以及生成不使用自定義消息、服務(wù)的程序do_not_use_local_messages_programcatkin包,那么CMakeLists.txt應(yīng)該寫成:
# 構(gòu)建時依賴項(xiàng)
find_package(catkin REQUIRED COMPONENTS
message_generation
std_msgs
# 聲明要構(gòu)建哪些消息
add_message_files(FILES
MyMessage1.msg
MyMessage2.msg)
# 聲明構(gòu)建哪些服務(wù)
add_service_files(FILES
MyService.srv)
# 聲明生成上述消息、服務(wù)需要依賴的消息以及服務(wù)
generate_messages(DEPENDENCIES
std_msgs
sensor_msgs)
# 聲明運(yùn)行時依賴項(xiàng)
catkin_package(CATKIN_DEPENDS
message_runtime
std_msgs sensor_msgs)
# 聲明構(gòu)建生成的可執(zhí)行文件名稱以及依賴項(xiàng)
add_executable(message_program src/main.cpp)
add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
# 聲明構(gòu)建不需要使用自定義消息、服務(wù)的可執(zhí)行文件
add_executable(does_not_use_local_messages_program src/main.cpp)
add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})
如果要構(gòu)建響應(yīng),則在包中“action”目錄中創(chuàng)建有一個名為“MyAction.action”的文件,則必須將actionlib_msgs添加到使用catkin進(jìn)行find_package的組件列表中,然后在CMakeList.txt中加上下面兩行:
add_action_files(FILES
MyAction.action)
此外,該包必須具有對actionlib_msgs的構(gòu)建依賴。
九、啟用Python模塊支持
如果catkin包使用一些Python模塊,您應(yīng)該創(chuàng)建一個setup.py文件,并在調(diào)用catkin_python_setup()之后調(diào)用generate_messages()和catkin_package()。
十、單位測試
有一個catkin-specific宏用于處理名為catkin_add_gtest()的基于gtest的單元測試。
catkin_add_gtest(myUnitTest test / utest.cpp)
十一、可選步驟:指定可安裝的目標(biāo)
構(gòu)建后,構(gòu)建目標(biāo)通常被放置在catkin工作區(qū)中。但有時候我們希望將目標(biāo)安裝到系統(tǒng)其他地方(有關(guān)安裝路徑的信息可以在REP 122中找到),以便其他人或本地文件夾可以使用它們來測試。換句話說,如果你想要做一個“make install”的代碼,你需要指定目標(biāo)應(yīng)該生成到哪里。
這是使用CMake install()函數(shù)作為參數(shù)完成的:
目標(biāo) - 目標(biāo)是安裝
ARCHIVE DESTINATION - 靜態(tài)庫和DLL(Windows).lib存根
LIBRARY DESTINATION - 非DLL共享庫和模塊
RUNTIME DESTINATION - 可執(zhí)行目標(biāo)和DLL(Windows)樣式共享庫
舉個栗子:
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
除了這些標(biāo)準(zhǔn)目標(biāo),一些文件必須安裝到特殊文件夾。即一個包含Python綁定的庫必須安裝到不同的文件夾中才能在Python中導(dǎo)入:
install(TARGETS python_module_library
ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
)
11.1 安裝Python可執(zhí)行腳本
對于Python代碼,安裝規(guī)則看起來是不同的,因?yàn)闆]有使用add_library()和add_executable()函數(shù),以便CMake確定哪些文件是目標(biāo)以及它們是什么類型的目標(biāo)。 而是在您的CMakeLists.txt文件中使用以下內(nèi)容:
catkin_install_python(PROGRAMS scripts/myscript
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
有關(guān)安裝python腳本和模塊的詳細(xì)信息以及文件夾布局的最佳做法,請參見catkin手冊。
如果您只安裝Python腳本并且不提供任何模塊,則不需要創(chuàng)建上述setup.py文件,也不需要調(diào)用catkin_python_setup()。
11.2 安裝頭文件
標(biāo)題文件也必須安裝到“include”文件夾中,這通常是通過安裝整個文件夾的文件來完成的(可選地,按文件名模式過濾,不包括SVN子文件夾)。 這可以通過如下所示的安裝規(guī)則完成:
install(DIRECTORY include/{CATKIN_PACKAGE_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
或者如果包含的子文件夾與包名稱不匹配:
install(DIRECTORY include/
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
11.3 安裝roslaunch文件或其他資源
其他資源(如啟動文件)可以安裝到
$ {CATKIN_PACKAGE_SHARE_DESTINATION
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)
參考文章:
1.ROS中的CMakeLists.txt
2.catkinCMakeLists.txt