http://community.bwbot.org/topic/182/
这是一个比较复杂的问题,但是有时候会有莫名其妙的编译错误,在找错误的过程中会非常需要了解这个过程。
首先说一下.in文件。在catkin的目录中有许多.in文件
这些都是模板文件,以/opt/ros/kinetic/share/catkin/cmake/templates/env.sh.in
为例
下面是源文件
#!/usr/bin/env sh
# generated from catkin/cmake/templates/env.sh.in
if [ $# -eq 0 ] ; then
/bin/echo "Usage: env.sh COMMANDS"
/bin/echo "Calling env.sh without arguments is not supported anymore. Instead spawn a subshell and source a setup file manually."
exit 1
fi
# ensure to not use different shell type which was set before
CATKIN_SHELL=sh
# source @SETUP_FILENAME@.sh from same directory as this file
_CATKIN_SETUP_DIR=$(cd "`dirname "$0"`" > /dev/null && pwd)
. "$_CATKIN_SETUP_DIR/@SETUP_FILENAME@.sh"
exec "$@"
这个是生成后的文件
#!/usr/bin/env sh
# generated from catkin/cmake/templates/env.sh.in
if [ $# -eq 0 ] ; then
/bin/echo "Usage: env.sh COMMANDS"
/bin/echo "Calling env.sh without arguments is not supported anymore. Instead spawn a subshell and source a setup file manually."
exit 1
fi
# ensure to not use different shell type which was set before
CATKIN_SHELL=sh
# source setup.sh from same directory as this file
_CATKIN_SETUP_DIR=$(cd "`dirname "$0"`" > /dev/null && pwd)
. "$_CATKIN_SETUP_DIR/setup.sh"
exec "$@
可以看出源文件中的@parameter@
都被替换成了实际的参数。通过调用源文件加上对应的参数就可以生成不同的目标文件了。每个ROS的软件包都要通过这种方式生成对应的xxxConfig.cmake等文件。
下面开始说大致的过程
catkin_make
实际和下面的指令是等效的
$ cd ~/catkin_ws
$ cd src
$ catkin_init_workspace
$ cd ..
$ mkdir build
$ cd build
$ cmake ../src -DCMAKE_INSTALL_PREFIX=../install -DCATKIN_DEVEL_PREFIX=../devel
$ make
所以最先被编译的是那个在src
下的公用的CMakeList.txt
文件
这个文件的大部分内容都是在找catkin这个包的位置。最后执行一个cmake函数catkin_workspace
。这个函数在/opt/ros/kinetic/share/catkin/cmake/catkin_workspace.cmake
文件中定义。这个函数对catkin_make
执行时的参数进行解析,比如CATKIN_WHITELIST_PACKAGES
。然后开始遍历工作空间中的文件夹,如果文件夹中有package.xml
文件就将其当作一个软件包。同时对每个软件包调用add_subdirectory
。add_subdirectory
是一个cmake
的内置函数,会调用这个文件夹内的CMakeList.txt
文件。这样就开始了每个软件包的编译了。
所以如果你的catkin_make
本身出了问题就要在这个过程中去Debug。
下面说说每个软件包的编译过程。以geometry2/tf2_geometry_msgs
这个包为例
其CMakeList.txt
文件如下
cmake_minimum_required(VERSION 2.8.3)
project(tf2_geometry_msgs)
find_package(orocos_kdl)
find_package(catkin REQUIRED COMPONENTS geometry_msgs tf2_ros tf2)
find_package(Boost COMPONENTS thread REQUIRED)
# Issue #53
find_library(KDL_LIBRARY REQUIRED NAMES orocos-kdl HINTS ${orocos_kdl_LIBRARY_DIRS})
catkin_package(
LIBRARIES ${KDL_LIBRARY}
INCLUDE_DIRS include
DEPENDS orocos_kdl
CATKIN_DEPENDS geometry_msgs tf2_ros tf2)
include_directories(include
${catkin_INCLUDE_DIRS}
)
link_directories(${orocos_kdl_LIBRARY_DIRS})
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
catkin_python_setup()
if(CATKIN_ENABLE_TESTING)
find_package(catkin REQUIRED COMPONENTS geometry_msgs rostest tf2_ros tf2)
add_executable(test_geometry_msgs EXCLUDE_FROM_ALL test/test_tf2_geometry_msgs.cpp)
target_link_libraries(test_geometry_msgs ${catkin_LIBRARIES} ${GTEST_LIBRARIES} ${orocos_kdl_LIBRARIES})
add_rostest(${CMAKE_CURRENT_SOURCE_DIR}/test/test.launch)
add_rostest(${CMAKE_CURRENT_SOURCE_DIR}/test/test_python.launch)
if(TARGET tests)
add_dependencies(tests test_geometry_msgs)
endif()
endif()
首先是指明cmake版本,项目名称,软件包依赖之类的常见操作。然后执行了catkin_package
这个函数。这个函数做了大量的工作。catkin_package
在/opt/ros/kinetic/share/catkin/cmake/catkin_package.cmake
文件中定义。devel和build文件夹内的内容基本都是由其生成的。
这个函数解析package.xml
文件,提取出里面的参数,由这些参数给find_package
和pkg-config
生成对应的配置文件。这样其他的对这个软件包有依赖的程序就可以方便的使用了。
所以需要重点分析的就是catkin_package
这个函数。
对于编译程序最重要的就是头文件的位置和链接库的位置。也就是include directory 和 library directory。这个函数就是在为软件包配置这些参数。它自动的根据依赖关系把依赖的程序的头文件和链接库目录加入到当前的变量中。然后根据这些参数和对应的模板文件生成对应的配置文件。比如根据/opt/ros/kinetic/share/catkin/cmake/templates/pkgConfig.cmake.in
生成软件包的pkgConfig.cmake
文件。这样这个软件包就可以被其他的软件包用find_package
找到。
如果在编译过程中发现有软件包的路径出了问题,那么就要在这个过程去debug。很有可能是生产pkgConfig.cmake
时的参数不对。最终可能是依赖包中的软件包路径问题(有挺多的ros软件包都是把路径写死的,这样很不好)。
由catkin_package
生成的文件最终会被安装到devel和build文件夹下。下面就具体看一下生成了哪些文件。
下面是一个一般的devel的文件结构。devel是develop的缩写,所以这就是开发环境。bin
内是被编译的可执行文件。lib
是pkg.pc
文件和python的库文件。pkg.pc
是pkg-config
的配置文件(关于pkg-config可以看另外一篇帖子)。 include
用来放置头文件。 share
是放置生成的pkgConfig.cmake
文件的,在cmake文件中find_package
就会用到这些文件。
下面是其中的一个例子。
对于build
文件夹,生成是一些编译中的中间文件,比如用来存储一些环境变量之类的文件。这个文件夹意义不大。
如果你在编译过程中出现问题可以去看build文件夹中各种文件内部的参数,可以方便的定位到可能出现问题的位置。
总结一下整个编译的过程
- 执行
catkin_make
- 执行
catkin_workspace
。解析catkin_make
的参数同时遍历整个工作空间把所有的有package.xml
的文件夹添加进软件包列表里面。对每个软件包执行add_subdirectory
- 执行每个软件包内部的
CMakeList.txt
文件。 - 执行
catkin_package
。解析package.xml
文件,载入对应的参数。根据依赖参数,载入对应的软件包参数。根据载入参数生成当前软件包的配置文件。