一、ros命令 catkin_init_workspace 分析
创建ros工作空间的步骤:
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
我们可以看到一行打印的消息:(请记住这行消息,下面会用到)
$ Creating symlink "~/catkin_ws/src/CMakeLists.txt" pointing to "/opt/ros/kinetic/share/catkin/cmake/toplevel.cmake"
1. catkin_init_workspace 命令运行
catkin_init_workspace 命令会在当前src目录下创建一个 CMakeLists.txt 文件,这是一个用于catkin工作空间的顶层CMakeLists.txt文件。该文件的主要作用是搜索catkin,并将其包含到工作空间中。CMakeLists.txt的源文件:在这里
catkin_init_workspace命令实则为运行一个py文件,它的文件位置如下:
$ which catkin_init_workspace
$ /opt/ros/kinetic/bin/catkin_init_workspace
打开这个py文件,可以看到:
from catkin.init_workspace import init_workspace # noqa: E402
###
###...省略..###
###
workspace = os.path.abspath(args.workspace)
if not os.path.isdir(workspace):
parser.error('Workspace "%s" does not exist' % workspace)
try:
init_workspace(workspace)
我们可以在我们的电脑中找到包含函数init_workspace的py文件,如下:
$ sudo find / -name init_workspace*
$ /opt/ros/kinetic/lib/python2.7/dist-packages/catkin/init_workspace.py
该文件位置:在这里 也可以找到
打开这个init_workspace.py文件,可以看到:
def _symlink_or_copy(src, dst):
try:
os.symlink(src, dst)
print('Creating symlink "%s" pointing to "%s"' % (dst, src))
根据这里的print()函数和上面的那条打印消息我们可以知道 dst 和 src 分别是:
$ ~/catkin_ws/src/CMakeLists.txt
$ /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake
它这里使用了Python中os.symlink()函数,用于创建符号链接,src是我们要创建符号链接的源文件或目录的路径,而dst则是新的符号链接的路径。符号链接其实是一种类似于快捷方式的文件系统对象,它可以指向另一个文件或目录。
其实这个CMakeLists.txt的内容和toplevel.cmake的内容完全一样,ros在编译使用CMakeLists.txt时,实际上相当于使用了toplevel.cmake,然后再调用这个目录下面的其它cmake函数完成编译。
2. 命令运行后产生的文件分析
我们再来看看这个 CMakeLists.txt 文件,打开这个文件我们可以看到:
cmake_minimum_required(VERSION 3.0.2)
project(Project)
set(CATKIN_TOPLEVEL TRUE)
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
)
execute_process()是CMake提供的一个命令,用于在构建过程中执行外部命令或脚本,RESULT_VARIABLE 表示命令的返回值将会被保存在这个 _res 变量中,可以用来判断命令是否执行成功。OUTPUT_VARIABLE 表示命令的标准输出将会被保存在这个 _out 变量中,可以用来获取命令的输出结果。
首先这里使用execute_process命令执行catkin_find_pkg命令来查找catkin包,而catkin_find_pkg命令实则在执行一个py文件,用于查找给定名称的ROS软件包并返回其相对路径。该py文件位置是:
$ which catkin_find_pkg
$ /usr/bin/catkin_find_pkg
我们知道 ${CMAKE_SOURCE_DIR} 就是指当前代码的目录,又因为当前这个CMakeLists.txt文件是通过软链接得到的,所以使用execute_process的命令完全可以看成在 /opt/ros/kinetic/share/catkin/cmake 目录下面执行了下面的命令:
$ catkin_find_pkg catkin /opt/ros/kinetic/share/catkin/cmake
这个命令的意思就是说在 /opt/ros/kinetic/share/catkin/cmake 下寻找catkin包。这将不会找到 catkin包的(大家可以试试这个命令),因为 catkin 包是指 /opt/ros/kinetic/share/catkin/ 这个文件夹。你可能会说找不到怎么办,别急往下看,
if(_res EQUAL 0)
set(catkin_EXTRAS_DIR "${CMAKE_SOURCE_DIR}/${_out}/cmake")
include(${catkin_EXTRAS_DIR}/all.cmake NO_POLICY_SCOPE)
add_subdirectory("${_out}")
如果成功找到catkin,_res 的结果会是 0,则会将catkin_EXTRAS_DIR设置为catkin的cmake目录,并通过include命令包含all.cmake文件,以让其在同一作用域中运行。然后,它使用add_subdirectory命令将catkin包含到工作空间中。
当然这里未找到catkin包,将会跳到下面的代码:在这里 58行
find_package(catkin QUIET
NO_POLICY_SCOPE
PATHS ${catkin_search_path}
NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
如果未找到catkin,_res 的结果会是 1,则会搜索 catkin_search_path 环境变量中指定的路径,以查找catkin工作空间。如果找到,则使用find_package命令将catkin包含到工作空间中。这里的 ${catkin_search_path} 是指目录 /opt/ros/melodic
其中,参数catkin表示要查找的软件包的名称。QUIET参数表示在查找过程中不输出任何信息,NO_POLICY_SCOPE参数表示不检查任何CMake策略,而是使用默认值。PATHS参数指定了查找路径,这里使用了变量${catkin_search_path},它是一个由catkin构建系统定义的变量,表示catkin软件包的搜索路径。NO_DEFAULT_PATH参数表示不使用默认的查找路径,NO_CMAKE_FIND_ROOT_PATH参数表示不在根目录中查找。
找到catkin软件包后,将会调用一个CMake的函数,如下:
catkin_workspace()
上面这个函数定义在一个cmake文件中,在这里
打开这个文件后,这里我们先来看函数 em_expand()的内容
assert(catkin_EXTRAS_DIR)
em_expand(
${catkin_EXTRAS_DIR}/templates/order_packages.context.py.in
${CMAKE_CURRENT_BINARY_DIR}/catkin_generated/order_packages.py
${catkin_EXTRAS_DIR}/em/order_packages.cmake.em
${CMAKE_CURRENT_BINARY_DIR}/catkin_generated/order_packages.cmake
)
这里的 ${catkin_EXTRAS_DIR} 是指 /opt/ros/melodic/share/catkin/cmake
这里的 ${CMAKE_CURRENT_BINARY_DIR} 是指 ~/catkin_ws/build (其实就是你工作空间下面的build文件夹)
该函数 em_expand()也是定义在一个cmake文件中(在这里),它的作用是在你工作空间下面的 build/catkin_generated/ 里,根据模板文件生成 order_packages.py 和 order_packages.cmake 文件。
该函数执行结束后,会在 order_packages.cmake 文件中会生成一些变量比如 CATKIN_ORDERED_PACKAGES 它用来存储你 src 下面所有功能包的名字,具体请看下面:
CATKIN_ORDERED_PACKAGES 是一个列表,包含了按顺序排列的所有包的名称。
CATKIN_ORDERED_PACKAGE_PATHS 是一个列表,包含了按顺序排列的所有包的路径。
CATKIN_ORDERED_PACKAGES_IS_META 是一个列表,里面是布尔类型值,表示这些包是否为元包。一般都为False。元包是一个虚拟包,它不包含任何源代码,但是可以用于组合和管理其他包。
CATKIN_ORDERED_PACKAGES_BUILD_TYPE 是一个列表,表示所有包的构建类型。构建类型一般都为catkin。
知道上面变量的类型后,我们创建个功能包,就可以知道catkin_workspace()函数后面的情况,大部分功能包可以看成下面这种情况:
foreach(index RANGE ${range})
if(${build_type} MATCHES catkin)
message(STATUS "~~ - ${name}")
endforeach()
foreach(index RANGE ${range})
elseif(${build_type} MATCHES catkin)
message(STATUS "+++ processing catkin package: '${name}'")
message(STATUS "==> add_subdirectory(${path})")
add_subdirectory(${path})
endforeach()
这里先是打印了功能包信息的日志,然后使用 add_subdirectory() 命令将功能包的 CMakeLists.txt 文件所在的目录添加到当前项目中,使其成为当前项目的一部分。
到这里ros命令 catkin_init_workspace 分析结束。