ROS 1.0 学习笔记(6)CMakeList.txt 使用说明

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. 指定编译目标

所编译的目标可以采用多种形式,但是通常它们代表以下两种可能性之一:

  • Executable Target - programs we can run

  • Library Target - libraries that can be used by executable targets at build and/or runtime

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(, , , ... )

例子:

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)

你可能感兴趣的:(ROS,学习笔记)