• DevOps cmake教程(三)find_package和pkg_check_modules


    find_package

    find_package用于加载第三方库,可以将需要的部分指定为组件,例如使用包 nodelet:
    find_package(catkin REQUIRED COMPONENTS nodelet)。例如,使用 Boost 时加载线程组件:

    1
    find_package(Boost REQUIRED COMPONENTS thread)

    使用Find_Package寻找模块时,每一个模块都会产生如下变量:

    1
    2
    3
    _FOUND
    _INCLUDE_DIR
    _LIBRARY or _LIBRARIES

    如果_FOUND 为真,需要把_INCLUDE_DIR 加入到INCLUDE_DIRECTORIES中,_LIBRARY 加入到TARGET_LINK_LIBRARIES中。

    module 模式

    find_package将先到 module 路径下查找 Find<name>.cmake。首先它搜索 ${CMAKE_MODULE_PATH}中的所有路径,然后搜索 /usr/share/cmake-3.5/Modules.比如find_package(Boost)搜索的文件是/usr/share/cmake-3.5/Modules/FindBoost.cmake

    如果在 CMakeLists.txt 中没有下面的指令:

    1
    set(CMAKE_MODULE_PATH  "Findxxx.cmake文件所在的路径")

    那么 cmake 不会搜索CMAKE_MODULE_PATH指定的路径,此时 cmake 会搜索第二优先级的路径.

    config 模式

    如果按照 module 模式未找到,cmake 将会查找 <Name>Config.cmake 或 <lower-case-name>-config.cmake 文件。cmake 会优先搜索 xxx_DIR 指定的路径。如果在 CMakeLists 中没有下面的指令:

    1
    set(xxx_DIR  "xxxConfig.cmkae文件所在的路径")

    那么 cmake 就不会搜索 xxx_DIR 指定的路径.而是到/usr/local/lib/cmake/xxx/中搜索.比如/usr/local/lib/cmake/yaml-cpp/yaml-cpp-config.cmake

    对于catkin_make,它会搜索 ROS Package 的安装目录,比如std_msgs对应的文件路径在/opt/ros/kinetic/share/std_msgs/cmake/std_msgsConfig.cmake.这两个文件是库文件安装时自己安装的,将自己的路径硬编码到其中。

    cmake 找到任意一个之后就会执行这个文件,然后这个文件执行后就会设置好一些 cmake 变量。比如下面的变量(NAME 表示库的名字,比如可以用 Opencv 代表 Opencv 库):

    1
    2
    3
    4
    <NAME>_FOUND    # 布尔量
    <NAME>_INCLUDE_DIRS   or   <NAME>_INCLUDES
    <NAME>_LIBRARIES   or   <NAME>_LIBRARIES   or    <NAME>_LIBS
    <NAME>_DEFINITIONS

    我们可以在 CMakeList 中用下面代码检验find_package的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    find_package(but_velodyne  REQUIRED)
    if (but_velodyne_FOUND)
      MESSAGE (STATUS   "definitions:      ${ButVELODYNE_DEFINITIONS}")
      MESSAGE (STATUS   "include dirs:    ${ButVELODYNE_INCLUDE_DIRS}")
      MESSAGE (STATUS   "lib dirs:     ${ButVELODYNE_LIBRARY_DIRS}")
    
      include_directories(${ButVELODYNE_INCLUDE_DIRS})
      target_link_libraries (helloworld ${ButVELODYNE_LIBRARY_DIRS})
    else()
      MESSAGE (STATUS " but_velodyne   not found")
    endif(but_velodyne_FOUND)

    如果 cmake 在两种模式提供的路径中没有找到对应的 Findxxx.cmake 和 xxxConfig.cmake 文件,此时系统就会提示最上面的那些错误信息。

    ROS 中的 find_package

    ROS 的catkin_make会自动搜寻 ROS 的相关目录,不必用 set 指定搜索目录.但是用纯 cmake 寻找其他第三方库,比如 OpenCV 时,就需要在find_package前面去设置 opencv 的文件夹在哪里,例如:set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "~/opencv3.1_install/lib/share/OpenCV/" )

    ROS 中常常需要多个 package,比如 roscpp,rospy,std_msgs。我们可以写成:

    1
    2
    find_package(roscpp REQUIRED)
    find_package(std_msgs REQUIRED)

    这样的话,每个依赖的 package 都会产生几个变量,这样很不方便。所以还有另外一种方式:

    1
    2
    3
    4
    find_package(catkin REQUIRED COMPONENTS
           roscpp
           std_msgs
    )

    这样,它会把所有 pacakge 里面的头文件和库文件等等目录加到一组变量上,比如catkin_INCLUDE_DIRS,这样,我们就可以用这个变量查找需要的文件了,最终就只产生一组变量了。


    REQUIRED 参数:其含义是指是否是工程必须的,表示如果报没有找到的话,cmake 的过程会终止,并输出警告信息。对应于 Find.cmake 模块中的 NAME_FIND_REQUIRED 变量。

    COMPONENTS 参数:在 REQUIRED 选项之后,或者如果没有指定 REQUIRED 选项但是指定了 COMPONENTS 选项,在它们的后面可以列出一些与包相关(依赖)的部件清单

    Qt 库的情况

    以上问题都还简单,问题是在 ROS 中调用 Qt 的情况,以 Core 模块为例,我们有下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    find_package(Qt5 COMPONENTS Core Xml)
    target_link_libraries(bin   Qt5::Core Qt5::Xml)
    
    if(Qt5Core_FOUND)
        MESSAGE(STATUS "##### ${Qt5Core_VERSION}")
        MESSAGE(STATUS "##### ${Qt5Core_INCLUDE_DIRS}")
        MESSAGE(STATUS "##### ${Qt5Core_LIBRARIES}")
    endif(Qt5Core_FOUND)

    运行结果是:

    1
    2
    3
    --  5.5.1
    --  /usr/include/x86_64-linux-gnu/qt5/;    /usr/include/x86_64-linux-gnu/qt5/QtCore;    /usr/lib/x86_64-linux-gnu/qt5//mkspecs/linux-g++-64
    --  Qt5::Core

    首先注意:能使用 Qt5 的 CMake 最低版本是 3.1.0,Qt 库的宏规则实际仍然符合 cmake,不同的地方在于不需要`命令,只要<code>target_link_libraries</code>就足够,它会自动添加相应的<code>include directories</code>, <code>compile definitions</code>等等.</p><p>Qt5Core对应的cmake文件在Linux上有两个位置:</p><figure class="highlight plain"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br/><span class="line">2</span><br/><span class="line">3</span><br/></pre></td><td class="code"><pre><span class="line">/home/user/Qt5.11.1/5.11.1/gcc_64/lib/cmake/Qt5Core/Qt5CoreConfig.cmake</span><br/><span class="line"></span><br/><span class="line">/usr/lib/x86_64-linux-gnu/cmake/Qt5Core/Qt5CoreConfig.cmake</span><br/></pre></td></tr></tbody></table></figure><p>经过测试,<code>CMakeLists</code>中起作用的应当是第二个,应当是安装Qt时,在此目录生成了文件.这个路径与环境变量<code>Qt5_DIR</code>相近,在CMakeLists中用MESSAGE函数能看到:<code>/usr/lib/x86_64-linux-gnu/cmake/Qt5</code></p><h3 id="Boost"><a href="#Boost" class="headerlink" title="Boost"></a>Boost</h3><p>Boost比较特殊,cmake对它有特别照顾,使用命令<code>cmake --help-module FindBoost</code>可以看到极为详细的使用方法.使用Boost有时要加上<code>REQUIRED COMPONENTS XXX</code>,这是在搜索已经编译的库,但不会检查只有头文件的库.比如<code>thread</code>和<code>system</code>要加入COMPONENTS但asio不需要.</p><p>cmake中使用Boost的filesystem,thread模块:</p><figure class="highlight plain"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br/><span class="line">2</span><br/><span class="line">3</span><br/><span class="line">4</span><br/><span class="line">5</span><br/><span class="line">6</span><br/><span class="line">7</span><br/></pre></td><td class="code"><pre><span class="line">find_package(Boost COMPONENTS system filesystem thread REQUIRED)</span><br/><span class="line"></span><br/><span class="line">target_link_libraries(mytarget </span><br/><span class="line"> ${Boost_FILESYSTEM_LIBRARY}</span><br/><span class="line"> ${Boost_SYSTEM_LIBRARY}</span><br/><span class="line"> ${Boost_THREAD_LIBRARY}</span><br/><span class="line">)</span><br/></pre></td></tr></tbody></table></figure><p>必须得加上<code>system</code>模块</p><h2 id="pkg-check-modules"><a href="#pkg-check-modules" class="headerlink" title="pkg_check_modules"></a>pkg_check_modules</h2><p><code>pkg_check_modules</code>是 CMake 自己的 pkg-config 模块的一个用来简化的封装:你不用再检查 CMake 的版本,加载合适的模块,检查是否被加载,等等,参数和传给 find_package 的一样:先是待返回变量的前缀,然后是包名(pkg-config 的)。这样就定义了<code>&lt;prefix&gt;_INCLUDE_DIRS</code>和其他的这类变量,后续的用法就与<code>find_package</code>一致。</p><p>当安装某些库时(例如从RPM,deb或其他二进制包管理系统),会包括一个后缀名为 pc 的文件,它会放入某个文件夹下(依赖于系统设置,例如,Linux 为该库文件所在文件夹/lib/pkgconfig),并把该子文件夹加入<code>pkg-config</code>的环境变量<code>PKG_CONFIG_PATH</code>作为搜索路径。<br/><code>pkg_check_modules</code>实质上是检测系统中的 pkg-config 是否存在指定的 .pc 文件。</p><p>在我的电脑上执行<code>echo $PKG_CONFIG_PATH</code>,结果是:</p><figure class="highlight plain"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br/></pre></td><td class="code"><pre><span class="line">/home/user/Robot/workspace/devel/lib/pkgconfig:/opt/ros/kinetic/lib/pkgconfig:/opt/ros/kinetic/lib/x86_64-linux-gnu/pkgconfig</span><br/></pre></td></tr></tbody></table></figure><p>在<code>bash.rc</code>里没有设置,但是能获得这个环境变量,这是因为我们的环境变量里设置了</p><figure class="highlight plain"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br/><span class="line">2</span><br/></pre></td><td class="code"><pre><span class="line">source /opt/ros/kinetic/setup.bash</span><br/><span class="line">source /home/user/Robot/workspace/devel/setup.bash</span><br/></pre></td></tr></tbody></table></figure><br/>在cmake中使用pkg_check_modules`时,就会去上面的路径里搜索 pc 文件,例如

    1
    2
    3
    4
    # bfl (Bayesian Filtering Library)是一个使用pkg-config的第三方库
    # 先搜索cmake自己的PkgConfig模块,才能使用pkg_check_modules
    find_package(PkgConfig)
    pkg_check_modules(BFL REQUIRED orocos-bfl)

    其中PkgConfig的路径在/opt/ros/kinetic/share/ros/core/rosbuild/FindPkgConfig.cmake

    搜索对应库文件,发现在以下路径:

    1
    2
    /opt/ros/kinetic/lib/liborocos-bfl.so
    /opt/ros/kinetic/lib/pkgconfig/orocos-bfl.pc

    现在我们可以获得对应的宏,使用这个库了:

    1
    2
    include_directories(${BFL_INCLUDE_DIRS})
    link_directories(${BFL_LIBRARY_DIRS})

    参考:
    CMake Manual For Qt
    Boost 的编译库列表

  • 相关阅读:
    实时控制软件设计-读书笔记
    1月9号
    第一组咖啡机器人部分图形设计
    《构建之法:现代软件工程》阅读笔记
    《实时控制软件设计》第三周作业
    实时控制软件设计第二次作业
    《实时控制软件设计》团队项目个人总结
    《实时控制软件设计》团队项目第三天工作日志
    《实时控制软件设计》团队项目第三天工作日志
    《实时控制软件设计》团队项目第二天工作日志
  • 原文地址:https://www.cnblogs.com/lidabo/p/16661072.html
Copyright © 2020-2023  润新知