# toplevel CMakeLists.txt for a catkin workspace
# catkin/cmake/toplevel.cmake//src下的CMakeLists.txt是由toplevel生成的 cmake_minimum_required(VERSION 2.8.3)
//找到合适的cmake编译工具 set(CATKIN_TOPLEVEL TRUE)
# search for catkin within the workspace set(_cmd "catkin_find_pkg" "catkin" "${CMAKE_SOURCE_DIR}") execute_process(COMMAND ${_cmd} RESULT_VARIABLE _res OUTPUT_VARIABLE _out
ERROR_VARIABLE _err
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE )
if(NOT _res EQUAL 0 AND NOT _res EQUAL 2)
# searching fot catkin resulted in an error
string(REPLACE ";" " " _cmd_str "${_cmd}") message(FATAL_ERROR "Search for 'catkin' in workspace failed (${_cmd_str}): ${_err}")
endif()
# include catkin from workspace or via find_package()
if(_res EQUAL 0)
set(catkin_EXTRAS_DIR "${CMAKE_SOURCE_DIR}/${_out}/cmake")
# include all.cmake without add_subdirectory to let it operate in same scope
include(${catkin_EXTRAS_DIR}/all.cmake NO_POLICY_SCOPE) add_subdirectory("${_out}") else()
# use either CMAKE_PREFIX_PATH explicitly passed to
CMake as a command line argument
# or CMAKE_PREFIX_PATH from the environment
if(NOT DEFINED CMAKE_PREFIX_PATH)
if(NOT "$ENV{CMAKE_PREFIX_PATH}" STREQUAL "") string(REPLACE ":" ";"
CMAKE_PREFIX_PATH $ENV{CMAKE_PREFIX_PATH})
endif()
endif()
# list of catkin workspaces
set(catkin_search_path "")
foreach(path ${CMAKE_PREFIX_PATH})
if(EXISTS "${path}/.catkin")
list(FIND catkin_search_path
${path} _index)
if(_index EQUAL -1)
list(APPEND catkin_search_path ${path})
endif()
endif()
endforeach()
# search for catkin in all workspaces
set(CATKIN_TOPLEVEL_FIND_PACKAGE TRUE) find_package(catkin QUIET
NO_POLICY_SCOPE
PATHS ${catkin_search_path}
NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) unset(CATKIN_TOPLEVEL_FIND_PACKAGE)
if(NOT catkin_FOUND)
message(FATAL_ERROR "find_package(catkin) failed. catkin was neither found in the workspace nor in the CMAKE_PREFIX_PATH. One reason may be that no ROS setup.sh was sourced before.")
endif()
endif()
catkin_workspace()
这个文件的大部分内容都是在找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文件。这样就开始了每个软件包的编译了。
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文件,载入对应的参数。根据依赖参数,载入对应的软件包参数。根据载入参数生成当前软件包的配置文件。