catkin_make:编译之后devel与build的关系

这是一个比较复杂的问题,但是有时候会有莫名其妙的编译错误,在找错误的过程中会非常需要了解这个过程。

首先说一下.in文件。在catkin的目录中有许多.in文件
catkin_make:编译之后devel与build的关系_第1张图片
这些都是模板文件,以/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 @[email protected] from same directory as this file
_CATKIN_SETUP_DIR=$(cd "`dirname "$0"`" > /dev/null && pwd)
. "$_CATKIN_SETUP_DIR/@[email protected]"
exec "$@"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这个是生成后的文件

#!/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 "$@
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以看出源文件中的@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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

所以最先被编译的是那个在src下的公用的CMakeList.txt文件
这个文件的大部分内容都是在找catkin这个包的位置。最后执行一个cmake函数catkin_workspace。这个函数在/opt/ros/kinetic/share/catkin/cmake/catkin_workspace.cmake文件中定义。这个函数对catkin_make执行时的参数进行解析,比如CATKIN_WHITELIST_PACKAGES。然后开始遍历工作空间中的文件夹,如果文件夹中有package.xml文件就将其当作一个软件包。同时对每个软件包调用add_subdirectoryadd_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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

首先是指明cmake版本,项目名称,软件包依赖之类的常见操作。然后执行了catkin_package这个函数。这个函数做了大量的工作。catkin_package/opt/ros/kinetic/share/catkin/cmake/catkin_package.cmake文件中定义。devel和build文件夹内的内容基本都是由其生成的。
这个函数解析package.xml文件,提取出里面的参数,由这些参数给find_packagepkg-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的缩写,所以这就是开发环境。
0_1492221561279_Screenshot from 2017-04-15 09-59-11.png
bin内是被编译的可执行文件。libpkg.pc文件和python的库文件。pkg.pcpkg-config的配置文件(关于pkg-config可以看另外一篇帖子)。 include用来放置头文件。 share是放置生成的pkgConfig.cmake文件的,在cmake文件中find_package就会用到这些文件。下面是其中的一个例子。
0_1492221851390_Screenshot from 2017-04-15 10-04-03.png
对于build文件夹,生成是一些编译中的中间文件,比如用来存储一些环境变量之类的文件。这个文件夹意义不大。
如果你在编译过程中出现问题可以去看build文件夹中各种文件内部的参数,可以方便的定位到可能出现问题的位置。

总结一下整个编译的过程
1. 执行catkin_make
2. 执行catkin_workspace。解析catkin_make的参数同时遍历整个工作空间把所有的有package.xml的文件夹添加进软件包列表里面。对每个软件包执行add_subdirectory
3. 执行每个软件包内部的CMakeList.txt文件。
4. 执行 catkin_package。解析package.xml文件,载入对应的参数。根据依赖参数,载入对应的软件包参数。根据载入参数生成当前软件包的配置文件。

你可能感兴趣的:(ROS,CMake)