如何使用cmake找到第三方ROS库

简介

本文以常用点云匹配算法库ndt_omp为例,探究在一个cmake ROS工程中使用第三方ROS库的方法与背后的机制。

如何引入第三方算法库

首先ndt_omp 是一个基于ROS组织的算法库(ROS包),这可以通过工程中的package.xml看出来。要使用该库,首先我们将该工程移动到主工程的src文件中,和其他的ROS包处与平行的关系。若需要在另一个ROS工程中使用ndt_omp库,则在该ROS工程的CMakeLists.txt中添加如下:

find_package(catkin REQUIRED COMPONENTS
...
  ndt_omp
  ...
)

对于find_package(),我们知道它其实是去查找.cmake配置文件并加载配置文件中关于库的信息,优先进入MODULE模式去查找FindXXX.cmake文件,如果找不到则会进入config模式去指定路径下查找XXXConfig.cmake文件。

那么,在当前这个实例中,执行cmake后,find_package(catkin)会找到/opt/ros/melodic/share/catkin/cmake/catkinConfig.cmake文件进行执行,在该文件中,对catkin_INCLUDE_DIRScatkin_LIBRARIES,catkin_LIBRARY_DIRS等环境变量进行了构造,而将ndt_omp添加到find_package(catkin REQUIRED COMPONENTS …) 中,那么ndt_omp的头文件、库文件等信息就会被加入到catkin_INCLUDE_DIRS、catkin_LIBRARIES等环境变量中,然后将这些信息通过target_link_libraries、include_directories等方式进行设置就可以成功找到包了。

那么ndt_omp包通过find_package(catkin REQUIRED COMPONENTS …) 加入到catkin_INCLUDE_DIRS、catkin_LIBRARIES等环境变量的信息又是如何被设置的呢?这就是靠catkin_package()命令,在ndt_omp包的CMakeLists.txt中就可以看到:

  catkin_package(
    INCLUDE_DIRS include
    LIBRARIES ndt_omp
  )

catkin_package() 的参数如下:

	INCLUDE_DIRS:声明给其他 package 的 include 路径。
    LIBRARIES:声明给其他 package 的库。
    CATKIN_DEPENDS:本包依赖的 catkin package。
    DEPENDS:本包依赖的非 catkin package。
    CFG_EXTRAS:其它配置参数。

如果其他功能包采用find_package(catkin REQUIRED COMPONENTS XXX)使用当前功能包XXX, XXX包中使用catkin_package()设置的 include 路径和库以及DEPENDS 依赖将会包含在catkin_INCLUDE_DIRS 和 catkin_LIBRARIES等变量中 。
catkin_package() 的另一个作用,就是将该包编译生成的文件按照ROS工程的模板进行放置,例如会将可执行文件生成到devel/lib/XXX中。
那么还有一个疑问,我们知道catkin_package()设置的信息会被设置到catkin_INCLUDE_DIRS 和 catkin_LIBRARIES等变量中,那么这背后是如何实现的呢? 请看下面的内容。

find_package(catkin REQUIRED COMPONENTS)细节

find_package(catkin REQUIRED COMPONENTS)将找到catkinConfig.cmake并将REQUIRED COMPONENTS传入进行执行,下面对catkinConfig.cmake的内容进行解析.

1、初始化环境变量

# XXXX don't overwrite catkin_* variables when being called recursively
if(NOT _CATKIN_FIND_ OR _CATKIN_FIND_ EQUAL 0)
  set(_CATKIN_FIND_ 0)
  if(catkin_FIND_COMPONENTS)
    set(catkin_INCLUDE_DIRS "")
    set(catkin_LIBRARIES "")
    set(catkin_LIBRARY_DIRS "")
    set(catkin_EXPORTED_TARGETS "")
  endif()
endif()
# increment recursion counter
math(EXPR _CATKIN_FIND_ "${_CATKIN_FIND_} + 1")

本ros工程第一次find_package(catkin)时,进入XX/catkinConfig.cmake后_CATKIN_FIND_没有定义,如上代码所示,将创建catkin_INCLUDE_DIRScatkin_LIBRARIES,catkin_LIBRARY_DIRS 等环境变量 并初始化为空。
而find_package()在后面处理依赖时也可能递归的触发XX/catkinConfig.cmake,例如tf依赖
find_package(catkin REQUIRED COMPONENTS tf)就会导致后续递归的触发, 为了保证递归触发XX/catkinConfig.cmake时环境变量不会执行初始化清0,设立了_CATKIN_FIND_变量,记录XX/catkinConfig.cmake的递归次数,并通过IF判断使得仅在第一次执行XX/catkinConfig.cmake时执行初始化了。

2、处理依赖

# find all components
if(catkin_FIND_COMPONENTS)
  message("catkin_FIND_COMPONENTS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  foreach(component ${catkin_FIND_COMPONENTS})
    string(TOLOWER "${component}" component_lower)
    # skip catkin since it does not make sense as a component
    if(NOT ${component_lower} STREQUAL "catkin")

      # get search paths from CMAKE_PREFIX_PATH (which includes devel space)
      set(paths "")
      foreach(path ${CMAKE_PREFIX_PATH})
        if(IS_DIRECTORY ${path}/share/${component}/cmake)
          list(APPEND paths ${path}/share/${component}/cmake)
        endif()
      endforeach()

      # find package component
      if(catkin_FIND_REQUIRED)
        # try without REQUIRED first
        find_package(${component} NO_MODULE PATHS ${paths}
          NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
        if(NOT ${component}_FOUND)
          # show better message to help users with the CMake error message coming up
          message(STATUS "Could not find the required component '${component}'. "
            "The following CMake error indicates that you either need to install the package "
            "with the same name or change your environment so that it can be found.")
          find_package(${component} REQUIRED NO_MODULE PATHS ${paths}
            NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
        endif()
      elseif(catkin_FIND_QUIETLY)
        find_package(${component} QUIET NO_MODULE PATHS ${paths}
          NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
      else()
        find_package(${component} NO_MODULE PATHS ${paths}
          NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
      endif()

      # append component-specific variables to catkin_* variables
      list_append_unique(catkin_INCLUDE_DIRS ${${component}_INCLUDE_DIRS})

      # merge build configuration keywords with library names to correctly deduplicate
      catkin_pack_libraries_with_build_configuration(catkin_LIBRARIES ${catkin_LIBRARIES})
      catkin_pack_libraries_with_build_configuration(_libraries ${${component}_LIBRARIES})
      list_append_deduplicate(catkin_LIBRARIES ${_libraries})
      # undo build configuration keyword merging after deduplication
      catkin_unpack_libraries_with_build_configuration(catkin_LIBRARIES ${catkin_LIBRARIES})

      list_append_unique(catkin_LIBRARY_DIRS ${${component}_LIBRARY_DIRS})
      list(APPEND catkin_EXPORTED_TARGETS ${${component}_EXPORTED_TARGETS})
    endif()
  endforeach()

  list_insert_in_workspace_order(catkin_INCLUDE_DIRS ${catkin_INCLUDE_DIRS})
  list_insert_in_workspace_order(catkin_LIBRARY_DIRS ${catkin_LIBRARY_DIRS})
endif()

当catkin有依赖的组件时,catkin_FIND_COMPONENTS 不为空, 当对组件的依赖是REQUIRED时,catkin_FIND_REQUIRED为true。遍历所有的组件并对属于catkin的组件执行find_package(),此时find_package会去catkin工作空间的devel/share文件夹中找对应组件的XXXConfig.cmake获取组件信息,例如对于ndt_omp,相应的文件是:
在这里插入图片描述
之后将获取的组件信息加入到catkin_INCLUDE_DIRScatkin_LIBRARIES,catkin_LIBRARY_DIRS环境变量中:

# append component-specific variables to catkin_* variables
 list_append_unique(catkin_INCLUDE_DIRS ${${component}_INCLUDE_DIRS})

 # merge build configuration keywords with library names to correctly deduplicate
 catkin_pack_libraries_with_build_configuration(catkin_LIBRARIES ${catkin_LIBRARIES})
 catkin_pack_libraries_with_build_configuration(_libraries ${${component}_LIBRARIES})
 list_append_deduplicate(catkin_LIBRARIES ${_libraries})
 # undo build configuration keyword merging after deduplication
 catkin_unpack_libraries_with_build_configuration(catkin_LIBRARIES ${catkin_LIBRARIES})

 list_append_unique(catkin_LIBRARY_DIRS ${${component}_LIBRARY_DIRS})
 list(APPEND catkin_EXPORTED_TARGETS ${${component}_EXPORTED_TARGETS})

那么,catkin组件的XXXConfig.cmake文件又是如何生成的呢?依然是通过 catkin_package()生成的!
拿ndt_omp来说,catkin_package()会使得编译时在catkin工作空间中的devel/share/ndt_omp/cmake/中生成ndt_ompConfig-version.cmake和ndt_ompConfig.cmake。

总结

通过find_package(catkin REQUIRED COMPONENTS XXX) 会找到catkinConfig.cmake文件并执行,在catkinConfig.cmake中又会对REQUIRED COMPONENTS执行find_package(),比如find_package(XXX),这又将
找到XXXConfig.cmake,并从中获取信息放置到catkin_INCLUDE_DIRS、catkin_LIBRARIES,catkin_LIBRARY_DIRS 等环境变量,而XXXConfig.cmake将通过catkin_package()进行生成,catkin_package()中通过参数设置的信息将传入到XXXConfig.cmake。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值