ROS学习笔记15(ROS/CMakeLists.txt文件)

原文链接: http://wiki.ros.org/catkin/CMakeLists.txt

1 概述

CMakeLists.txt文件是构建软件包所必备的文件 ,其描述了如何构建程序以及在哪里安装程序包。任何一个文件包通常都会包含一个或者多个CMakeLists.txt文件。CMakeLists.txt文件遵守了vanilla 标准,用于一个catkin项目,含有一定的约束条件。

2 整体结构和结构

CMakeListx.txt文件必须准守下面的这些守则,否则的话,程序包将不能被正确的构建。在配置文件中,下面的顺序很重要。

  • Required CMake Version (cmake_minimum_required)

  • Package Name (project())

  • Find other CMake/Catkin packages needed for build (find_package())

  • Enable Python module support (catkin_python_setup())

  • Message/Service/Action Generators (add_message_files(), add_service_files(), add_action_files())

  • Invoke message/service/action generation (generate_messages())

  • Specify package build info export (catkin_package())

  • Libraries/Executables to build (add_library()/add_executable()/target_link_libraries())

  • Tests to build (catkin_add_gtest())

  • Install rules (install())

3 CMake的版本

每一个CMakeLists.txt文件都是以所需要的CMake的版本开始。Catkin需要的CMake版本至少2.8.3。

cmake_minimum_required(VERSION 2.8.3)

4 软件包的名字

然后的条款是由CMake 项目函数指定包的名称。比如,我们构建了一个叫做robot_brain的安装包。

project(robot_brain)

需要注意的是,在之后的CMake脚本中,你可以使用${PROJECT_NAME}引用项目名称。

5 找到依赖的CMake包

我们使用CMake find_package函数来指定一些必备的用来构建我们项目的CMake软件包。通常存在有至少一个被依赖的软件包:catkin。

find_package(catkin REQUIRED)

如果你的项目也依赖于其他的一些软件包,可以将他们自动的被转化为catkin的组件;并不是直接使用find_package于这些软件包,而是将它们转化为组件,这会令任务更简单。例如,假如你想使用nodelet软件包。

find_package(catkin REQUIRED COMPONENTS nodelet)

注意:find_package 的组件功能仅用于当你想要找到构建项目的软件包,不要添加 项目runtime时的依赖。

你也可以这样做:

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

当然,这是一种很不方便的做法。

5.1  find_package()做了什么

如果一个软件包被CMake通过find_package函数找到,那么系统会自动的生成一系列描述软件包的CMake 环境变量。这些环境变量会稍后被CMake脚本使用。与此同时,这些环境变量描述了软件包会把头文件导出到了那里,以及哪些被软件包依赖的库和这些库的路径。环境变量的命名通常是:_:

_FOUND 当库被找到的时候,被设置为true;否则,设置为false;

_INCLUDE_DIRS 或者 _INCLUDES 包含了软件包导出的路径

_LIBRARIES 或者 _LIBS 被软件包导出的库

_DEFINITIONS ?

5.2 Catkin Packages 为什么要被指定为组件?

Catkin软件包并不是catkin真正的组件。不过设计出来的CMake的组件功能却可以明显的节约你的输入时间。

对于catkin 软件包,当使用find_package找到它们并把它们当做catkin组件,则这种方法的优势很明显,而且其创建了一系列有着catkin_前缀的环境变量。举个例子,当想要在程序中使用nodelet软件包的时候,一种比较建议的方法如下:

find_package(catkin REQUIRED COMPONENTS nodelet)

这意味着包含路径,库,等等被nodelet软件包导出的变量也被添加到了catkin_variables。比如,catkin_INCLUDE_DIRS不仅仅包含了catkin的路径同时也包含了nodelet的路径。这在之后用很有用。

当然我们也可以使用下面的这种方法:

find_package(nodelet)

然而这意味着nodelet的路径,库等等并不会添加到catkin_variables中。

这会产生了nodelet_INCLUDE_DIRS,nodelet_LIBRARIES等等一系列的变量。

同样的变量也可以使用下面方法创建:

find_package(catkin REQUIRED COMPONENTS nodelet)

5.3 Boost

如果使用C++和Boost,你需要援引find_package()于Boost并且需要特别指定Boost的那些方面。例如,当你想要使用Boost threads时,你要如下这样做:

find_package(Boost REQUIRED COMPONENTS thread)

6 catkin_package()

catkin_package()是一个catkin_provided 的CMake macro文件。构建系统必须要指定catkin特定的信息,而系统又用于生成类pkg-config和CMake文件。

在使用add_library()和add_executable()函数声明目标之前,必须的调用这个函数。这个函数有5个可选的参数。

INCLUDE_DIRS - 包含了导出软件包头文件的路径

LIBRARIES - 项目导出的库

CATKIN_DEPENDS - 这个项目依赖的其他catkin项目

DEPENDS - 这个项目依赖的其他non-catkin项目。更详细的讲解,参考this explanation.

CFG_EXTRAS - 其他额外的一些配置选择

比较详细的macros 说明文件可以参考 here.

举个例子:

catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

其中inlcude 中是软件包导出的头文件路径。CMake环境变量${PROJECT_NAME}就是之前传递给project()函数的参数,在这个例子中,它就是“robot_brain”。"roscpp" + "nodelet"是需要构建这个软件包所必须的catkin软件包,而"eigen" + "opencv"是构建这个软件包的系统依赖,也就是非non-catkin软件包。

7 指定生成目标

有很多的方式可以生成目标文件。但通常具有代表性的是下面。

1.可执行目标。我们可以运行的程序。

2.库目标。被可执行目标在构建或者运行时间时使用的库。

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 包含路径和库路径

在指定目标之前,需要指定可以构建目标的资源位置,特别是头文件和库:

  • 包含路径 - 在哪里可以找到正在构建的代码(在C / C ++中最常见)的头文件
  • 库路径 - 可执行目标构建的库位于何处?
  • 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 目录也是路径的一部分(其中包含了头文件的路径)。

7.3.2 link_directories()

CMake link_directories()函数可用于添加其他库路径,但并不建议这样做。所有catkin和CMake软件包在find_packaged时自动添加其链接信息。只需链接到target_link_libraries()中的库即可。

例:

link_directories(〜/ my_libs)

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

7.4 可执行目标

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

7.5 库目标

add_library()这个 CMake函数用来指定库来构建。默认情况下,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 消息,服务和操作目标

ROS中的messages(.msg),services(.srv)和action(.action)文件在构建和使用ROS包之前,需要进行特殊的预处理器构建步骤。这些宏是生成指定编程语言的文件的关键,以便可以使用所选编程语言中的消息,服务和操作。构建系统可以使用所有可用的生成(例如gencpp,genpy,genlisp等)。

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

  • add_message_files

  • add_service_files

  • add_action_files

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

 generate_messages()

8.1 重要的先决条件和限制

  • 这些macros必须在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()必须包含有包messagese_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})

8.2 例子

软件包在名为“msg”的目录下分别有两个消息文件:“ MyMessage1.msg ”和“ MyMessage2.msg ” ,这些消息依赖于std_msgs和sensor_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的构建(build)依赖性。

9  启用Python模块支持

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

catkin_python_setup()

使用上述函数需要在调用generate_messages()和catkin_package()之前。

10 单元测试

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

if(CATKIN_ENABLE_TESTING)
  catkin_add_gtest(myUnitTest test / utest.cpp)
endif()

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

经过构建之后,需要将目标放置到catkin工作空间的开发空间中。但是,我们通常希望在系统中安装目标(有关安装路径的信息可以在REP 122中找到 ),以便其他人或本地文件夹可以使用它们来测试系统级安装。换句话说,如果您希望能够对代码进行“make install”,则需要指定目标应该结束的位置。

这是由CMake install()函数完成的,该函数有下面的几个参数:

  • TARGETS - 那个被安装的目标

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

11.1 安装Python的可执行脚本

对于Python代码,安装规则看起来不同,这是因为没有使用add_library()和add_executable()函数,以便CMake确定哪些文件是目标以及它们是什么类型的目标。

在CMakeLists.txt文件中使用以下内容:

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

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

如果只安装Python脚本并且不提供任何模块,则既不需要创建上面提到的setup.py文件(需要使用的时候,再进行查阅),也不需要调用catkin_python_setup()。

11.2 安装头文件

头文件也必须安装到“include”文件夹中,这通常通过安装整个文件完成(可以选择使用文件名模式排除无需安装的子文件,比如排除SVM后缀的子文件)。可以通过如下的安装规则完成:

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文件和其他的一些资源

其他的一些资源,比如launchfiles可以被安装这个文件下:${CATKIN_PACKAGE_SHARE_DESTINATION}:

install(DIRECTORY launch/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
  PATTERN ".svn" EXCLUDE)

12 补充:另一种解释

构建配置文件(CMakeLists.txt)中的每一项如下所示。第一条是操作系统中安装的cmake的最低版本。由于它目前被指定为版本2.8.3,所以如果使用低于此版本的cmake,则必须更新版本。

cmake_minimum_required(VERSION 2.8.3)

project项是功能包的名称。只需使用用户在package.xml中输入的功能包名即可。请注意,如果功能包名称与package.xml中的标记中描述的功能包名称不同,则在构建时会发生错误,因此需要注意。

project(my_first_ros_pkg)

find_package项是进行构建所需的组件包。目前,roscpp和std_msgs被添加为依赖包。如果此处没有输入功能包名称,则在构建时会向用户报错。换句话说,这是让用户先创建依赖包的选项。

find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)

以下是使用ROS以外的功能包时使用的方法。例如,使用Boost时,必须安装system功能包。功能如前面的说明,是让用户先创建依赖功能包的选项。

find_package(Boost REQUIRED COMPONENTS system)

catkin_python_setup( )选项是在使用Python,也就是使用rospy时的配置选项。其功能是调用Python安装过程setup.py。

catkin_python_setup()

add_message_files是添加消息文件的选项。FILES将引用当前功能包目录的msg目录中的*.msg文件,自动生成一个头文件(*.h)。在这个例子中,我们将使用消息文件Message1.msg和Message2.msg。 

add_message_files(
FILES
Message1.msg
Message2.msg
)

add_service_files是添加要使用的服务文件的选项。使用FILES会引用功能包目录中的srv目录中的*.srv文件。在这个例子中,用户可以选择使用服务文件Service1.srv和Service2.srv。

add_service_files(
FILES
Service1.srv
Service2.srv
)

 generate_messages是设置依赖的消息的选项。此示例是将DEPENDENCIES选项设置为使用std_msgs消息包。

generate_messages(
DEPENDENCIES
std_msgs
)

generate_dynamic_reconfigure_options是使用dynamic_reconfigure时加载要引用的配置文件的设置。

generate_dynamic_reconfigure_options(
cfg/DynReconf1.cfg
cfg/DynReconf2.cfg
)

以下是catkin 构建选项。INCLUDE_DIRS表示将使用INCLUDE_DIRS后面的内部目录include的头文件。LIBRARIES表示将使用随后而来的功能包的库。CATKIN_DEPENDS后面指定如roscpp或std_msgs等依赖包。目前的设置是表示依赖于roscpp和std_msgs。DEPENDS是一个描述系统依赖包的设置。

catkin_package(
INCLUDE_DIRS include
LIBRARIES my_first_ros_pkg
CATKIN_DEPENDS roscpp std_msgs
DEPENDS system_lib
)

include_directories是可以指定包含目录的选项。目前设定为${catkin_INCLUDE_DIRS},这意味着将引用每个功能包中的include目录中的头文件。当用户想指定一个额外的include目录时,写在${catkin_INCLUDE_DIRS}的下一行即可。

include_directories(
${catkin_INCLUDE_DIRS}
)

 add_library声明构建之后需要创建的库。以下是引用位于my_first_ros_pkg功能包的src目录中的my_first_ros_pkg.cpp文件来创建my_first_ros_pkg库的命令。

add_library(my_first_ros_pkg
src/${PROJECT_NAME}/my_first_ros_pkg.cpp
)

add_dependencies是在构建该库和可执行文件之前,如果有需要预先生成的有依赖性的消息或dynamic_reconfigure,则要先执行。以下内容是优先生成my_first_ros_pkg库依赖的消息及dynamic reconfigure的设置。

add_dependencies(my_first_ros_pkg ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

 add_executable是对于构建之后要创建的可执行文件的选项。以下内容是引用src/my_first_ros_pkg_node.cpp文件生成my_first_ros_pkg_node可执行文件。如果有多个要引用的*.cpp文件,将其写入my_first_ros_pkg_node.cpp之后。如果要创建两个以上的可执行文件,需追加add_executable项目。

add_executable(my_first_ros_pkg_node src/my_first_ros_pkg_node.cpp)

如前面描述的add_dependencies一样,add_dependencies是一个首选项,是在构建库和可执行文件之前创建依赖消息和dynamic reconfigure的设置。下面介绍名为my_first_ros_pkg_node的可执行文件的依赖关系,而不是上面提到的库。在建立可执行文件之前,先创建消息文件的情况下会经常用到。 

add_dependencies(my_first_ros_pkg_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries是在创建特定的可执行文件之前将库和可执行文件进行链接的选项。 

target_link_libraries(my_first_ros_pkg_node
${catkin_LIBRARIES}
)

 

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