ROS学习之CMakeLists.txt

概述

文件CMakeLists.txt是构建软件包的CMake构建系统的输入。任何与CMake兼容的软件包都包含一个或多个CMakeLists.txt文件,该文件描述如何构建代码以及安装在哪里。用于catkin项目的CMakeLists.txt文件是一个标准的普通CMakeLists.txt文件,只不过有一些额外的约束。

 

总体结构和顺序

您的CMakeLists.txt文件必须遵循此格式,否则您的包将无法正确构建。配置顺序很重要!

  1. 所需CMake版本cmake_minimum_required

  2. 软件包名称project()

  3. 查找构建所需的其他CMake / Catkin 软件包find_package()

  4. 启用Python模块支持catkin_python_setup()

  5. 消息/服务/动作生成器add_message_files(),add_service_files(),add_action_files()

  6. 消息/服务/动作生成generate_messages()

  7. 指定包构建的消息导出catkin_package()

  8. 要建立的库/可执行文件add_library()/ add_executable()/ target_link_libraries())(target_link_libraryies通常情况下要有)

  9. 测试建立catkin_add_gtest()

  10. 安装规则install()

 

CMake版本

每个catkin CMakeLists.txt文件必须以需要的CMake版本开始。Catkin需要2.8.3或更高版本。

cmake_minimum_required(VERSION 2.8.3

软件包名称

下一个指令是由CMake 项目功能指定的包的名称让我们说我们正在制作一个名为robot_brain的软件包

#软件包名
project(robot_brain)

请注意,在CMake中,您可以随时在CMake脚本中随时使用变量$ {PROJECT_NAME } 来引用项目名称

查找相关的CMake包

我们需要使用CMake find_package函数指定需要找到哪些其他CMake包来构建我们的项目总是至少有一个依赖于catkin:

find_package(catkin REQUIRED)

如果您的项目依赖于其他包,它们将自动变成catkin的组件(以CMake为单位)。而不是在这些软件包上使用find_package,如果您将它们指定为组件,它将使更轻松。例如,如果您使用package nodelet

find_package(catkin REQUIRED COMPONENTS nodelet)

注意:您应该只需要find_package组件来构建标志。您不应添加运行时依赖关系。

你也可以:

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

但是,你会看到这是一个不方便的事情。

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 - ?

为什么Catkin包被指定为组件?

Catkin包不是catkin的真正组成部分。相反,CMake的组件功能被用于catkin的设计,从而为您节省打字时间。

对于catkin包,如果find_package作为catkin的组件,这是有利的,因为使用catkin_前缀创建了一组环境变量例如,让我们说您在代码中使用package nodelet查找包的推荐方法是:

find_package(catkin REQUIRED COMPONENTS nodelet)

这意味着由nodelet导出的include路径,库等也附加到catkin_变量。例如,catkin_INCLUDE_DIRS不仅包含catkin的include路径,还包含了对于nodelet的include路径!这将在以后派上用场。

我们可以自己选择find_package nodelet:

find_package(nodelet)

这意味着nodelet路径,库等不会被添加到catkin_变量中。

这将导致nodelet_INCLUDE_DIRS,nodelet_LIBRARIES等。也使用相同的变量创建

Boost

如果使用C ++和Boost,您需要在Boost上调用find_package(),并指定用作组件的Boost的哪些方面。例如,如果你想使用Boost线程,你会说:

find_package(Boost REQUIRED COMPONENTS thread)

catkin_package()

catkin_package()是一个catkin提供的CMake宏。这是为构建系统指定catkin特定信息所必需的,后者又用于生成pkg-config和CMake文件。

在使用add_library()add_executable()声明任何目标之前必须调用此函数该函数有5个可选参数:

  • INCLUDE_DIRS - 包的导出包含路径

  • LIBRARIES - 从项目导出的库

  • CATKIN_DEPENDS - 该项目依赖的其他catkin项目

  • DEPENDS - 该项目所依赖的非catkin CMake项目。为了更好的理解,请看这个解释

  • CFG_EXTRAS - 其他配置选项

完整的宏文档可以在这里找到

举个例子:

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”是需要存在的用于构建/运行此程序包的系统依赖项。

 

指定构建目标

构建目标可以有多种形式,但通常它们代表两种可能之一:

  • 可执行目标 - 我们可以运行的程序
  • 库目标 - 可在构建和/或运行时由可执行目标使用的库

 

目标命名

非常重要的是注意,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})

包括路径和库路径

在指定目标之前,您需要指定可以为所述目标找到资源的位置,特别是头文件和库:

  • 包括路径 - 在哪里可以找到代码(最常见于C / C ++)的头文件
  • 库路径 - 哪些库位于该可执行目标建立对象?
  • include_directories(<dir1>,<dir2>,...,<dirN>)

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

include_directories()

include_directories的参数应该是find_package调用和需要包含的任何其他目录生成的* _INCLUDE_DIRS变量。如果您使用catkin和Boost,您的include_directories()调用应该如下所示:

 

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

第一个参数“include”表示包中的include /目录也是路径的一部分。

CMake link_directories()函数可用于添加额外的库路径,但是不推荐这样做。所有catkin和CMake软件包在find_packaged时都会自动添加链接信息。只需链接到target_link_libraries()中的库

例:

link_directories(~/my_libs)

请参阅本cmake的线程可以看到使用的详细例子target_link_libraries()link_directories() 

 

可执行目标

要指定必须构建的可执行目标,我们必须使用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。

库文件

add_library() CMake的功能是用来指定库来构建。默认情况下,catkin构建共享库。

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

使用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()自动引入

消息、服务和行动目标

消息(.msg),服务(.srv)和动作(.action)文件在ROS包构建和使用之前需要一个特殊的预处理器构建步骤。这些宏的要点是生成编程语言特定的文件,以便可以利用其选择的编程语言中的消息,服务和动作。构建系统将使用所有可用的生成器(例如gencpp,genpy,genlisp等)生成绑定。

提供了三个宏来分别处理消息,服务和动作:

  • add_message_files

  • add_service_files

  • add_action_files

这些宏之后必须跟随调用生成的宏:

 generate_messages()

重要先决条件/限制

  • 这些宏必须在catkin_package()宏之前,以便生成才能正常工作。

 find_package(catkin REQUIRED COMPONENTS ...)
 add_message_files(...)
 add_service_files(...)
 add_action_files(...)
 generate_messages(...)
 catkin_package(...)
 ...
  • 您的catkin_package()宏必须对message_runtime具有CATKIN_DEPENDS依赖关系。

catkin_package(
 ...
 CATKIN_DEPENDS message_runtime ...
 ...)
  • 您必须使用find_package()作为包message_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})

 

如果您的包在名为“ MyMessage1.msg ”和“ MyMessage2.msg ”的名为“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的构建依赖。

启用Python模块支持

如果您的ROS包提供了一些Python模块,您应该创建一个setup.py文件并调用

catkin_python_setup()

之前调用generate_messages()catkin_package()

单元测试

有一个catkin-specific宏用于处理名为catkin_add_gtest()的基于gtest的单元测试 

catkin_add_gtest(myUnitTest test / utest.cpp)

可选步骤:指定可安装的目标

构建时间之后,目标被放置在catkin工作空间的空间中。但是,通常我们希望将目标安装到系统中(有关安装路径的信息可以在REP 122中找到 ),以便其他人或本地文件夹可以使用它们来测试系统级安装。换句话说,如果你想要做一个“make install”的代码,你需要指定目标应该在哪里。

这是使用CMake install()函数作为参数完成的:

  • 目标 - 目标是安装

  • 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}
)

安装Python可执行脚本

对于Python代码,安装规则看起来是不同的,因为没有使用add_library()add_executable()函数,以便CMake确定哪些文件是目标以及它们是什么类型的目标。而是在您的CMakeLists.txt文件中使用以下内容:

 

 

有关安装python脚本和模块的详细信息以及文件夹布局的最佳做法,请参见catkin 手册

如果您只安装Python脚本并且不提供任何模块,则不需要创建上述setup.py文件,也不需要调用catkin_python_setup()

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

安装头文件

标题文件也必须安装到“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
)

安装roslaunch文件或其他资源

其他资源(如启动文件)可以安装到$ {CATKIN_PACKAGE_SHARE_DESTINATION

install(DIRECTORY launch/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
  PATTERN ".svn" EXCLUDE)
posted @ 2017-04-05 21:37  IQIT  阅读(2378)  评论(0编辑  收藏  举报