ROS1中每个PKG的配置都是在CMakeList.txt中,本文从官方 WiKi 资料中翻译而来。
文件CMakeLists.txt是CMake编译系统的配置文件,用于配置需要编译软件包。任何兼容CMake的软件包都包含一个或多个CMakeLists.txt文件,这些文件描述了如何编译代码以及将代码安装到何处。用于catkin 项目的CMakeLists.txt文件是标准的 vanilla CMakeLists.txt文件,并带有一些其他条件。
您的CMakeLists.txt文件必须遵循此格式,否则您的软件包将无法正确构建。配置中的顺序没有影响。
每个 catkin CMakeLists.txt文件必须以所需的CMake版本开头。Catkin需要版本2.8.3或更高版本。
cmake_minimum_required(VERSION 2.8.3)
下一项是由CMake项目功能指定的软件包名称。假设我们正在制作一个名为robot_brain的软件包。
project(robot_brain)
再声明project(project_name)后,可以在CMakeList.txt中,您可以通过使用变量
$ {PROJECT_NAME} 在脚本中的任何地方使用 package 名称。
然后,我们需要使用 CMake 中 find_package 函数来指定需要找到哪些其他CMake软件包来编译我们的项目。catkin始终至少存在一种依赖:
find_package(catkin REQUIRED)
如果您的项目依赖于其他功能包,则它们会自动转换为catkin的组件(以CMake类型)。如果您将这些软件包指定为组件,而不是在这些软件包上使用find_package,它将使工作变得更轻松。例如,如果您使用包nodelet。
find_package(catkin REQUIRED
COMPONENTS nodelet)
也可以用以下方式:
find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)
如果CMake通过find_package找到了一个包,它将影响到 与这个包有关的几个CMake环境变量的创建。这些环境变量在CMake脚本中后面可能被使用到。这些环境变量描述了软件包导出的头文件在哪里,源文件在哪里,软件包所依赖的库以及这些库的路径。
一般命名按照这个方式_:
< NAME >_FOUND - Set to true if the library is found, otherwise false
< NAME >_INCLUDE_DIRS or < NAME >_INCLUDES - The include paths exported
by the package
< NAME >_LIBRARIES or < NAME >_LIBS - The libraries exported by the
package
< NAME >_DEFINITIONS - ?
catkin 功能包并不是 catkin 的真正组成部分。而是在 catkin 设计中利用了 CMake 的组件功能,以节省大量的打字时间。
对 catkin 功能包来说,将 find_packages 的相关包当做 catkin 的组件,这是有利的,因为一系列的环境变量都是以 catkin_prefix 为前缀创建的。例如,假设我们在代码中使用功能包nodelet。推荐的查找软件包的方法是:
find_package(catkin REQUIRED COMPONENTS nodelet)
这意味着nodelet导出的 include 路径,库等也将附加到 catkin_variables 中。例如,catkin_INCLUDE_DIRS 不仅包含 catkin 的 include 路径,还包含 nodelet 的 include 路径!
我们也可以单独使用 find_package nodelet:
find_package(nodelet)
这意味着nodelet 路径,库等不会添加到catkin_变量中。
这将影响nodelet_INCLUDE_DIRS,nodelet_LIBRARIES等的使用。相同的变量可以通过以下方式创建:
find_package(catkin REQUIRED COMPONENTS nodelet)
如果使用C ++和Boost,则需要在Boost上调用find_package()并指定将Boost的哪些方面用作组件。例如,如果您想使用Boost线程:
find_package(Boost REQUIRED COMPONENTS thread)
catkin_package() 是由 catkin 提供的一种 CMake 宏。这是指定 catkin_specific 信息来编译系统所必须的,用以生成 pkg-config 和 CMake 文件。
此函数必须在调用 add_library()或 add_executable()声明任何目标之前调用。此函数有5个可选参数:
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv)
这表示导出功能包的头文件位于功能包文件夹中的文件夹“ include”中。
CMake 环境变量 ${PROJECT_NAME} 等于前面传给函数 project()的值,在这里就是"robot_brain"。
“roscpp” + "nodelet"是为了编译/运行这个功能包所必须添加的功能包依赖。
“eigen” + “opencv” 是为了编译/运行这个功能包所必须添加的系统依赖。
所编译的目标可以采用多种形式,但是通常它们代表以下两种可能性之一:
Executable Target - programs we can run
Library Target - libraries that can be used by executable targets at build and/or runtime
非常重要的一点是要注意,catkin中的编译目标名称必须唯一,而与编译/安装到的文件夹无关。
这是CMake的要求。但是,目标的唯一名称仅在CMake内部必需。
可以使用 set_target_properties()函数将目标重命名为其他目标。
例如:
set_target_properties(rviz_image_view
PROPERTIES OUTPUT_NAME image_view
PREFIX "")
这将在编译和安装输出中将目标rviz_image_view的名称更改为image_view。
通常将可执行文件和库的默认输出目录设置为合理的值,但在某些情况下必须对其进行自定义。
例如,必须将包含Python绑定的库放置在其他文件夹中,才能在Python中导入:
set_target_properties(python_module_library
PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
在指定目标之前,您需要指定可以在何处找到所述目标的资源,特别是头文件和库:
include_directories的参数应为调用find_package函数生成的 * _INCLUDE_DIRS 变量以及需要包含的任何其他目录。如果您使用的是catkin和Boost,则include_directories()调用应如下所示:
第一个参数“ include”表示功能包中的include /directory 也是路径的一部分。
CMake link_directories()函数可用于添加其他库路径,但是不建议这样做。当所有catkin和CMake软件包已经添加到find_package函数中时,会自动添加其链接信息。
只需链接到target_link_libraries()中的库:
link_directories(~/my_libs)
请参阅此 CMake线程,以查看在 link_directories()上使用t arget_link_libraries()的详细示例。
要指定必须编译的可执行目标,我们必须使用 CMake函数 add_executable()。
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。
CMake函数 add_library()用于指定要编译生成的库。默认情况下,catkin编译生成共享库。
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
使用 target_link_libraries()函数来指定可执行目标链接到的库。通常在调用 add_executable()之后完成此操作。如果找不到ros,请添加$ {catkin_LIBRARIES}。
语法;
target_link_libraries(, , , ... )
例子:
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()自动提取的。
ROS中的消息(.msg),服务(.srv)和操作(.action)文件需要特殊的预处理编译步骤,然后才能由ROS软件包对其编译和使用。这些宏的目的是生成特定于编程语言的文件,以便人们可以使用其选择编程语言对应的用消息,服务和操作功能。编译器支持各种可用的生成器(e.g. gencpp, genpy, genlisp, etc)。
提供了三个宏来分别处理消息,服务和操作:
generate_messages()
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
...
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
find_package(catkin REQUIRED COMPONENTS message_generation)
add_dependencies(some_target ${catkin_EXPORTED_TARGETS})
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
如果功能包的在名为“ msg”的目录中有两条消息“ MyMessage1.msg”和“ MyMessage2.msg”,并且这些消息依赖于std_msgs和sensor_msgs,在名为“ srv”的目录中有“ MyService.srv”的的服务,定义使用这些消息和服务的可执行文件 message_program,以及使用ROS某些部分但不使用此程序包中定义的消息/服务的 dos_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 添加到 find_packaged 的组件列表中,并在调用generate_messages(…)之前添加以下调用:
add_action_files(FILES
MyAction.action
)
此外,该程功能包必须对 actionlib_msgs 具有编译依赖(在package.xml中添加)。
如果您的ROS软件包提供了一些Python模块,则应创建setup.py文件并调用
catkin_python_setup()
catkin 中以 gtest 为基础的测试单元 , catkin_add_gtest()。
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(myUnitTest test/utest.cpp)
endif()
在编译后,生成的目标文件在 catkin 工作空间下的 devel 。但是,实际中需要将编译生成的目标文件放到系统(目标文件的安装路径)中,这样编译生成的目标文件就可以被其他用户使用,或者将生成的目标文件放在一个本地文件夹中,以用来测试系统级别的安装。换句话说,如果需要 make install 生成的目标文件,就必须指定生成文件的放置路径。
可以通过配置 CMake 中 install() function函数的相关参数来设置生成路径:
以生成共享库为例:
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
)
以生成执行文件为例:
install(TARGETS ${PROJECT_NAME}_node
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}
)
对 Python 而言,编译方式与 C++ 是不同的, CMake 决定编译输出路径 add_library() 和编译输出类型的函数 add_executable() 是没有用的。因此,按照如下方式在 CMakeLists.txt 进行配置:
catkin_install_python(PROGRAMS scripts/myscript
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
有关安装python脚本和模块的详细信息以及文件夹布局的最佳做法,可在 catkin手册 中找到。
头文件也必须安装到“ include”文件夹中,这通常是通过安装整个文件夹的文件来完成的( 可以按文件名模式过滤,但不包括SVN子文件夹)。这可以通过如下所示的安装规则来完成:
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
或包含在下面的子文件夹与软件包名称不匹配:
install(DIRECTORY include/
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
可以将其他资源(如 launch 文件)安装到$ {CATKIN_PACKAGE_SHARE_DESTINATION}:
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)