ROS1中每个PKG的配置都是在CMakeList.txt中,本文从官方 WiKi 资料中翻译而来。
1. 概览
文件CMakeLists.txt是CMake编译系统的配置文件,用于配置需要编译软件包。任何兼容CMake的软件包都包含一个或多个CMakeLists.txt文件,这些文件描述了如何编译代码以及将代码安装到何处。用于catkin 项目的CMakeLists.txt文件是标准的 vanilla CMakeLists.txt文件,并带有一些其他条件。
2. 整体架构
您的CMakeLists.txt文件必须遵循此格式,否则您的软件包将无法正确构建。配置中的顺序没有影响。
- 所需的CMake版本(cmake_minimum_required)
- 功能包名称(project())
- 查找编译所需的其他CMake / Catkin软件包(find_package())
- 启用Python模块支持(catkin_python_setup())
- 消息/服务/动作生成器(add_message_files(),add_service_files(),add_action_files())
- 调用消息/服务/动作生成(generate_messages())
- 指定软件包编译信息导出(catkin_package())
- 要编译的库/可执行文件(add_library()/ add_executable()/ target_link_libraries())
- 测试编译结果(catkin_add_gtest())
- 安装规则(install())
3. CMake版本
每个 catkin CMakeLists.txt文件必须以所需的CMake版本开头。Catkin需要版本2.8.3或更高版本。
cmake_minimum_required(VERSION 2.8.3)
4. 功能包名称
下一项是由CMake项目功能指定的软件包名称。假设我们正在制作一个名为robot_brain的软件包。
project(robot_brain)
再声明project(project_name)后,可以在CMakeList.txt中,您可以通过使用变量
$ {PROJECT_NAME} 在脚本中的任何地方使用 package 名称。
5. 查找依赖的CMake软件包
然后,我们需要使用 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)
5.1 find_package()有什么作用?
如果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 - ?
5.2 为什么将Catkin包装指定为组件?
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)
6. catkin_package()
catkin_package() 是由 catkin 提供的一种 CMake 宏。这是指定 catkin_specific 信息来编译系统所必须的,用以生成 pkg-config 和 CMake 文件。
此函数必须在调用 add_library()或 add_executable()声明任何目标之前调用。此函数有5个可选参数:
- INCLUDE_DIRS - The exported include paths (i.e. cflags) for the
package - LIBRARIES - The exported libraries from the project
- CATKIN_DEPENDS - Other catkin projects that this project depends on
- DEPENDS - Non-catkin CMake projects that this project depends on. For a better understanding, see this explanation.
- CFG_EXTRAS - Additional configuration options
完整的宏文档可在此处找到。
举个例子:
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” 是为了编译/运行这个功能包所必须添加的系统依赖。
7. 指定编译目标
所编译的目标可以采用多种形式,但是通常它们代表以下两种可能性之一:
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 Include 路径和 Library 路径
在指定目标之前,您需要指定可以在何处找到所述目标的资源,特别是头文件和库:
- Include Paths - Where can header files be found for the code (most common in C/C++) being built
- Library Paths - Where are libraries located that executable target build against?
- include_directories(, , …, )
- link_directories(, , …, )
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 /directory 也是路径的一部分。
7.3.2 link_directories()
CMake link_directories()函数可用于添加其他库路径,但是不建议这样做。当所有catkin和CMake软件包已经添加到find_package函数中时,会自动添加其链接信息。
只需链接到target_link_libraries()中的库:
link_directories(~/my_libs)
请参阅此 CMake线程,以查看在 link_directories()上使用t arget_link_libraries()的详细示例。
7.4 Executable Targets
要指定必须编译的可执行目标,我们必须使用 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。
7.5 Library Targets
CMake函数 add_library()用于指定要编译生成的库。默认情况下,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.Messages, Services, and Action Targets
ROS中的消息(.msg),服务(.srv)和操作(.action)文件需要特殊的预处理编译步骤,然后才能由ROS软件包对其编译和使用。这些宏的目的是生成特定于编程语言的文件,以便人们可以使用其选择编程语言对应的用消息,服务和操作功能。编译器支持各种可用的生成器(e.g. gencpp, genpy, genlisp, etc)。
提供了三个宏来分别处理消息,服务和操作:
- add_message_files
- add_service_files
- add_action_files
然后,必须在这些宏之后调用生成器以调用生成:
generate_messages()
8.1 Important Prerequisites/Constraints
- 为了使生成器正常工作,这些宏必须在 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 ...
...)
- 无论是单独使用还是作为catkin的组件使用,必须使用 find_package()用于功能包的 message_generation:
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_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中添加)。
9. 使能 Python 模块支持
如果您的ROS软件包提供了一些Python模块,则应创建setup.py文件并调用
catkin_python_setup()
10. 单元测试
catkin 中以 gtest 为基础的测试单元 , catkin_add_gtest()。
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(myUnitTest test/utest.cpp)
endif()
11. 可选步骤:指定编译输出目标路径
在编译后,生成的目标文件在 catkin 工作空间下的 devel 。但是,实际中需要将编译生成的目标文件放到系统(目标文件的安装路径)中,这样编译生成的目标文件就可以被其他用户使用,或者将生成的目标文件放在一个本地文件夹中,以用来测试系统级别的安装。换句话说,如果需要 make install 生成的目标文件,就必须指定生成文件的放置路径。
可以通过配置 CMake 中 install() function函数的相关参数来设置生成路径:
- TARGETS - which targets to install
- ARCHIVE DESTINATION - Static libraries and DLL (Windows) .lib stubs
- LIBRARY DESTINATION - Non-DLL shared libraries and modules
- RUNTIME DESTINATION - Executable targets and DLL (Windows) style shared libraries
以生成共享库为例:
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}
)
11.1 编译 Python 执行脚本
对 Python 而言,编译方式与 C++ 是不同的, CMake 决定编译输出路径 add_library() 和编译输出类型的函数 add_executable() 是没有用的。因此,按照如下方式在 CMakeLists.txt 进行配置:
catkin_install_python(PROGRAMS scripts/myscript
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
有关安装python脚本和模块的详细信息以及文件夹布局的最佳做法,可在 catkin手册 中找到。
11.2 安装头文件路径
头文件也必须安装到“ 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
)
11.3 安装roslaunch文件或其他资源
可以将其他资源(如 launch 文件)安装到$ {CATKIN_PACKAGE_SHARE_DESTINATION}:
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)