CMakeLists.txt文件是构建软件包所必备的文件 ,其描述了如何构建程序以及在哪里安装程序包。任何一个文件包通常都会包含一个或者多个CMakeLists.txt文件。CMakeLists.txt文件遵守了vanilla 标准,用于一个catkin项目,含有一定的约束条件。
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())
每一个CMakeLists.txt文件都是以所需要的CMake的版本开始。Catkin需要的CMake版本至少2.8.3。
cmake_minimum_required(VERSION 2.8.3)
然后的条款是由CMake 项目函数指定包的名称。比如,我们构建了一个叫做robot_brain的安装包。
project(robot_brain)
需要注意的是,在之后的CMake脚本中,你可以使用${PROJECT_NAME}引用项目名称。
我们使用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)
当然,这是一种很不方便的做法。
如果一个软件包被CMake通过find_package函数找到,那么系统会自动的生成一系列描述软件包的CMake 环境变量。这些环境变量会稍后被CMake脚本使用。与此同时,这些环境变量描述了软件包会把头文件导出到了那里,以及哪些被软件包依赖的库和这些库的路径。环境变量的命名通常是:
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)
如果使用C++和Boost,你需要援引find_package()于Boost并且需要特别指定Boost的那些方面。例如,当你想要使用Boost threads时,你要如下这样做:
find_package(Boost REQUIRED COMPONENTS thread)
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软件包。
有很多的方式可以生成目标文件。但通常具有代表性的是下面。
1.可执行目标。我们可以运行的程序。
2.库目标。被可执行目标在构建或者运行时间时使用的库。
非常重要的是需要注意,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(
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() 。
要指定必须构建的可执行目标,我们使用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(,,,... )
例:
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中的messages(.msg),services(.srv)和action(.action)文件在构建和使用ROS包之前,需要进行特殊的预处理器构建步骤。这些宏是生成指定编程语言的文件的关键,以便可以使用所选编程语言中的消息,服务和操作。构建系统可以使用所有可用的生成(例如gencpp,genpy,genlisp等)。
提供了三个宏来分别处理消息,服务和操作:
add_message_files
add_service_files
add_action_files
必须在调用生成的宏之后调用这些宏:
generate_messages()
这些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})
软件包在名为“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)依赖性。
如果你的ROS包提供了一些Python模块,你应该创建一个setup.py文件并调用
catkin_python_setup()
使用上述函数需要在调用generate_messages()和catkin_package()之前。
有一个特定于catkin的宏用于处理名为catkin_add_gtest()的基于gtest的单元测试。
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(myUnitTest test / utest.cpp)
endif()
经过构建之后,需要将目标放置到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}
)
对于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()。
头文件也必须安装到“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
)
其他的一些资源,比如launchfiles可以被安装这个文件下:${CATKIN_PACKAGE_SHARE_DESTINATION}:
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)
构建配置文件(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}
)