http://wiki.ros.org/catkin/CMakeLists.txt#msgs_srvs_actions
提要:文章顺序基于网址内容进行解读
另外:Catkin包就是我们平常所说的功能包,或者说ROS包,package等,再次强调这里大部分函数都是属于make工具,所以具体查书吧。
推荐资料《Cmake实践》
概述
1.CMakeLists.txt
该文件描述如何构建代码以及安装在哪里。
总结:
CMakeLists.txt 它的作用本质上就像出版社的排版工作,将我们准备要编译的内容整理好(也就是生成所谓的Makefile文件,不要搞错哦,cmake,不是编译,make 才是编译)
【cmake+ CMakeLists.txt=Makefile 排版】
【make+Makefile =目标文件 编译】
与VS对应就是所谓的解决方案,CMakeLists.txt对应的文件就是VS的 .sln后缀文件
Catkin是集成了CMake这个跨平台编译工具的工具,所以具有很多cmake特性。
想具体知道CMake是什么东东的话 《CMake实践》
强调:CMakeLists.txt与package.xml有很强的对应关系,CMakeList有的在package.xml都有而且能对应上,哪一个缺了都会报错。
2.CMakeLists.txt文件必须遵循此格式,否则您的包将无法正确构建。
Cmake最低版本 cmake_minimum_required()
工程名称 project()
查找构建所需的库文件或者catkin包 find_package()
启用Python模块支持 catkin_python_setup()
消息/服务/动作生成器 add_message_files()
add_service_files()
add_action_files()
调用消息/服务/动作生成 generate_messages()
Catkin包的导出、依赖信息 catkin_package()
建立库和可执行文件 add_library()
add_executable()
target_link_libraries()
编译测试 catkin_add_gtest()
安装规则 install() 安装和测试等要用的时候自己看
总结:
如果足够熟悉catkin的CMakeLists 的话可以不需要依赖命令生成,直接按照以下顺序填写 catkin说白了就是基于ROS的CMake,catkin是ROS定制版的CMake。
2.1.CMake版本 cmake_minimum_required()
函数意思是这个要编译我的cmake工具如果低于2.8.3,我就不让你编译。
例子:
cmake_minimum_required(VERSION2.8.3)
如果编译当前CMakeLists的Cmake它的版本低于2.8.3,则不编译。
2.2.工程名称 project()
就是平常用soildworks啊,或者用VS时我们说的新建的项目,工程名字,或者解决方案的名字。
例子:
project(home)
我当前的工程名字叫home,
注意:它与我们的节点名字,功能包名字没有一点关系。
还有,它对应package.xml的<name>…..<name>,<name>标签,最重要的是它代表的功能包的名字,如果它叫LL那么这个功能包名字叫LL
2.3.查找编译时所需的库文件或者catkin包 find_package()
首先,find_package()这个函数,或者叫宏。是cmake原有,它是用来寻找库文件,这没什么好说的,比如找Eigen库,Boost库,都要用,看书
这里主要注意几点的是
1. 对于ROS的功能包,肯定依赖于catkin,换句话说find_package(catkin REQUIRED)一定要有。
REQUIRED的作用表示我要寻找的依赖包对我来说很重要,如果找不到就不编译了,上面的例子就是说我找不到catkin就不玩了。
2. 当我们用它找某功能包(catkin包)时,它是可以直接用 find_package()例如:我现在在某个工作空间里有一个功能包——nodelet,我现在在编写一个新的功能包,它的CMakelists.txt,就需要这个nodelet包,(是指功能上需要这个包)
可以这样写
find_package(nodelet REQUIRED)
find_package(catkin REQUIRED)
3. 也可以将这个nodelet功能包当成要依赖的catkin的一个部件,元件,组件,反正就是一部分。
所以还可以这样写
find_package(catkin REQUIRED COMPONENTSnodelet)。
所以有两种找catkin包方式:
第一种: find_package(nodeletREQUIRED)
find_package(catkin REQUIRED)
第二种: find_package(catkinREQUIRED COMPONENTSnodelet) 把nodelet这个功能包当成catkin的一部分
注意Catkin包不是catkin这个依赖的真正组成部分。对于catkin包,如果find_package作为catkin的组件,这是有利的,因为使用catkin_前缀创建了一组环境变量。就是说,环境变量设置上简化了,不用我们想太多。如果找catkin包单独用findpackage的话,就会有属于这个包它自己环境变量,具体还是看书。
COMPONENTS它表示我们要找的包需要后面的这些库或者包,比如要找的catkin需要nodelet这个包。当然像前面说的,并非真的如此。
4. 所谓编译时所需的依赖,其实就是要进行编译时所需要的依赖包,findpackage()其实是对应了package.xml标签中的<build_depend>xxx</build_depend>特别在生成消息/服务时,运行和编译时的依赖都不一样,可以说findpackage()与catkin_package(CATKIN_DEPEND xxx)所跟的依赖是略有不同的,findpackage要的是message_generation,catkin_package要的是message_runtime
总结:
在编译时,所依赖的是一般的库文件(注意我的意思是,要找的东西不属于ROS的catkin包)的话,正常用 find_package(),比如:Boost库
find_package(Boost REQUIRED COMPONENTS thread)
在编译时,如果用该工程(该功能包)需要别的功能包的话,那么寻找的时候推荐将这个catkin包作为catkin这个依赖项的一个组件。即
find_package(catkin REQUIRED COMPONENTS 功能包名字)
findpackage找的其实是编译时的依赖,不是运行时的依赖,它对应package.xml中<build_depend>xxx</build_depend>标签。一个找编译时依赖,一个找运行时依赖。这一点在生成消息服务是尤其明显。
2.7.指定包的编译导出信息 catkin_package()
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv
)
catkin_package()这条指令有四条
1. 表示这个catkin包的头文件是导出到include这个文件夹,include位置是位于该功能包目录下(需要手动创建)就是当前编写的CMakeList.txt所在位置,与msg、srv、package.xml、CMakeList.txt共存的位置。与工作空间下(有src、build、devel的位置)devel文件下的include不同,那个有包含由msg文件生成的消息、服务的头文件。catkin_package的这个include我估计是存放以及读取这个功能包的头文件的地方。
2. 是将库导出来,就是做成一个库给别的功能包用。所以LIARARIES 和后面接的是工程名,或者是代表这个工程名的变量${PROJECT_NAME} 这个变量会替换成工程名
3. 是简单来说要填的内容就是该功能包运行时所需要的、所依赖的catkin包。它与package.xml中标签<run_depend>xxxx</run_depend>对应。运行和编译时的依赖都不一样,可以说findpackage()与catkin_package(CATKIN_DEPEND xxx)所跟的依赖是略有不同的,findpackage要的是message_generation,catkin_package要的是message_runtime
4. 这个包运行时所依赖的包如果不属于catkin包的话就要写在这里。简单来说,一样也是通过package.xml的run_depend标签所决定所确定,比如下面的opencv库 Eigen库
总结:
Catkin_package中的CATKIN_DEPEND后面填的是运行时的依赖,于package.xml的run_build标签对应,于findpackage刚好相反,一个找编译时依赖,一个找运行时依赖。这一点在生成消息是尤为明显。
例子:
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv ) 该文章最后结束前给出的例子可以验证这一点
7.编译指定目标
中间有部分省略。。。
关于7.1-7.2set_target_properties()这条指令属于cmake,用法翻书看看
7.3include路径和库文件路径
include_directories(<dir1>,<dir2>,...,<dirN>)
link_directories(<dir1>,<dir2>,...,<dirN>)
include_directories()(添加头文件的搜索路径,扩大搜索范围)
include_directories的参数应该是find_package调用生成的*_INCLUDE_DIRS变量。如果用findpackage()寻找catkin和Boost,然后用include_directories()找它俩的头文件,可以这样表达:
include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
注意:这里的include是文件夹 也是路径的一部分,你可以理解成它用了相对路径。
这段话的理解:
首先,include_directories()它是用来指定头文件的搜索路径。路径之间用空格分开,所以这里有三个路径,文件夹include的路径(第一个参数“include”它代表哪个include,目前还不清楚)变量Boost_INCLUDE_DIRS所代表的路径,和变量catkin_INCLUDE_DIRS所代表的路径。
寻找catkin 和boost 用findpackage(),
即findpackage(catkin REQUIRED)
和findpackage(Boost REQUIRED)会生成关于两个依赖项一堆变量,Boost_INCLUDE_DIRS和catkin_INCLUDE_DIRS 就是其中之二。
函数里的include我暂时理解有两个,一个是存放由.msg文件生成的头文件后所存放的那个include(即devel目录下的include)另一个是有catkin_package()中的INCLUDE_DIRS include所生成的,位置在功能包目录下的include(需要手动创建)
link_directories() (添加库的搜索路径,扩大搜索范围)
link_directories()函数用来添加非标准共享库的路径。(扩大库的搜索路径,搜索范围)
但是不推荐这样做(简单说不用考虑这个函数)。因为catkin和CMake软件包在find_packaged时都会自动添加链接信息。只需链接到target_link_libraries()中的库
add_executable() (将该功能包的cpp生成可执行文件)
例子:
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() (将功能包做成一个库给别人用)
是用来将目标文件生成一个库文件(造一个库给别人用)。默认情况下,生成共享库(即所谓的动态库,我们平常见到的share文件夹装的就是这些动态库,后缀名是xxx.so的库文件)
注意:静态库生成的是xxx.a文件,一般放在lib文件夹。
例子:
add_library(haha src/some_file.cpp)
该功能包将生成一个名为haha的动态库即 haha.so
add_library(haha STATICsrc/some_file.cpp)在中间加入STATIC就可以构建静态库
该功能包将生成一个名为haha的静态库即 haha.a
target_link_libraries() 是为目标添加链接库(调用别的库给自己用)
注意:前面link_directoies()这个函数作用是添加库文件的搜索路径,不一样,还有这通常放在add_executable()函数之后的,对于ROS包来讲,参数填$ {catkin_LIBRARIES}就足够了。
即:target_link_libraries(execu-name ${catkin_LIBRARIES })
句法:
target_link_libraries(<executable_Name>,<lib1>,<lib2>,... )
例:
add_executable(foo src/foo.cpp) foo.cpp生成一个foo.o的目标文件
add_library(moo src/moo.cpp) moo.cpp生成一个libmoo.so的动态库
target_link_libraries(foo moo) foo代表foo.o moo代表libmoo.so 将moo库加入到foo这个目标文件中。
请注意,在大多数用例中不需要使用link_directories(),因为信息通过find_package()自动导入。
还有target_link_libraries()是放在add_executable()和add_library()之后,如上例子所示。其实从参数构成就可以猜到为什么,我要先执行target_link_libraries的话哪来的目标文件,动态库。
消息,服务和行动目标
msg(.msg),srv(.srv)和act(.action)文件在ROS包构建和使用之前需要一个特殊的预处理器构建步骤。这些函数用于是生成特定语言格式文件。
提供了三个函数来分别处理消息,服务和动作:
add_message_files (
FILES
PP.msg
)
将msg文件夹下的pp.msg文件处理生成pp.h的头文件
add_service_files()同理可得
add_action_files()同理可得
注意:这些函数后面一定要有generate_messages(),还有只是使用别人包中的消息是不用上面几条函数。(最后的例子有说明这一点)
generate_messages()消息生成器
例子:
generate_messages(
DEPENDENCIES
std_msg
)
如果这个消息或服务的生成需要依赖某个包中的某个消息类/服务类。就需要将需要的那个类对应的功能包填上。例子中就是需要std_msg这个包的某个消息类(即某个xxx.msg文件)所以才填上这个包。
对于消息和服务怎样理解它们对包的依赖呢?其实就是看我在这个包上的xxx.msg文件它的内容上,它所定义的类是不是用了某一个功能包的生成的消息类。具体看消息这一章。
注意:测试证明如果你在你这个功能包下只是使用别的catkin包的消息类,例如geometry包下Twist.msg,是完全可以把generate_messages()注释掉,它是用来生成消息用的,对于不生成和只是使用别人包中现有的消息是不需要用的。
注意,一个catkin包是可以有多个消息/服务文件。
关于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_DEPENDSmessage_runtime ...
...)
就是说,在运行时,注意不是编译时,如果我要生成自己的消息/服务,那么就需要用这个message_runtime包,所以,可以猜到,跟findpackage()这个函数所填的依赖有点不一样,因为findpackage找的是编译时所需要的依赖即message_generation而不是运行时。
例如这样
find_package(catkin REQUIRED COMPONENTS message_generation)
当然如果你不要生成消息/服务就不必加入message_runtime。
注意:测试证明如果你在你这个功能包下只是用已有catkin包的消息类,例如geometry包下Twist.msg,是完全不用加入message_runtime,也就说明,它是用来生成消息用的,对于使用别人catkin包的消息是不影响。
其次package.xml文件也要包含对message_generation的编译时的依赖关系以及message_runtime运行时的依赖关系。
add_dependencies(some_target $ {catkin_EXPORTED_TARGETS})
对于该函数的一些使用,因为不怎么用,看书。
实例
CMakeLists.txt内容清单
工作空间:cat_ws
该功能包名字:rostext_alpha
工程名称:rostext.0.0.1
源文件:rostest.cpp
Cmake最低版本号:2.8.3
寻找所编译时需要的依赖项:
依赖项属于ros包:catkin、roscpp、rospy、message_runtime、std_msg、sensor_msgs (后面三个是与消息服务的依赖有关)
依赖项不属于ros包:Boost、Eigen、opencv、
msg目录:
两个消息文件——“ MyMessage1.msg ”和“ MyMessage2.msg ”
这些消息依赖于std_msgs和sensor_msgs
srv目录:
一个服务文件——“ MyService.srv ”
这个服务不依赖于任何服务类型
增加头文件搜索路径
生成一个动态库文件和静态库文件:
libhaha.so、libhello.a
生成可执行文件:
Kaka.exe
将一些库文件链接到生成的可执行文件
CMakeLists.txt的内容有以下:
cmake所需最低版本号
cmake_minimum_required(VERSION 2.8.3)
工程名称
project(rostext.0.01)
寻找编译时的依赖catkin、roscpp、rospy、message_generation、std_msgs、sensor_msgs,其中后三个作为catkin的组件,而不选择另起三个find_package()
find_package(catkin REQUIRED COMPONENTS roscpp rospy message_generation std_msgs sensor_msgs)
寻找编译时的依赖Boost、opencv、Eigen、
find_package(Boost)
find_package(Eigen)
find_package(opencv REQUIRED)##如果你认为opencv很重要,没了它你这个包就不行,就加REQUIRED关键字
要生成消息类的xxx.msg消息文件
add_message_files(
FILES
MyMessage1.msg
MyMessage2.msg
)##将生成两个头文件,MyMessage1.h和MyMessage2.h
要生成服务类的xxx.srv服务文件
add_service_files(
FILES
MyService.srv
)##将生成一个头文件,MyService.h
如果要生成消息或者服务就必须使用该函数
生成这些消息和服务所依赖的功能包
generate_messages(DEPENDENCIES std_msgs sensor_msgs)
声明这个catkin包的运行时依赖关系
catkin_package(
# LIBRARYIES hehe
CATKIN_DEPENDS roscpp rospy message_runtime std_msgs sensor_msgs
DEPENDS Boost opencv Eigen
)
添加头文件的搜索路径
为了能顺利找到Boost和opencv还有egien库
下面的变量是由findpackage()函数产生的。你也可以用绝对路径的方式寻找
下面一共添加了5个搜索路径,include文件夹 boost eigen opencv库还有catkin包(以及跟在它后面的作为组件的其他功能包)
Include_directories(include ${Boost_INCLUDE_DIRS} ${eigen_INCLUDE_DIRS}${opencv_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
生成静态库文件
hello将生成为libhello.a
add_library(hello /src/rostest.cpp)
生成动态库文件
haha将生成为libhaha.so
add_library(haha /src/rostest.cpp)
生成可执行文件
add_executable(kaka src/main.cpp)
将一些库文件链接到这个可执行文件
我将刚生成的两个库也加进去了,虽然没什么卵用,把boost opencv eigen库也加进去,你前面找了,不加进去那找来干嘛,也可以选择用绝对路径。Catkin包以及其他包肯定要有。强调这些包虽然是作为组件放到catkin包,但他们是同等的。如果独立分开用findpackage就要像opencv库那样加${roscpp_LIBRARIES}、${rospy_LIBRARIES}等等
target_link_libraries(kaka ${catkin_LIBRARIES} haha hello ${boost_LIBRARIES} ${opencv_LIBRARIES}${eigen_LIBRARIES})