第 22.4 節

第三方库依赖写法

0瀏覽次數0訪問次數--跳出率--平均停留

本节专门讲第三方库如何安装、如何在 CMake 中查找、如何放到模板的 Third-party dependencies 区块里。

本模板的原则是:

# ========================
# Third-party dependencies
# ========================
find_package(Eigen3 REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
)

也就是:

  1. 谁使用第三方库,谁写 find_package
  2. 第三方依赖集中放在本模块的 Third-party dependencies 区块。
  3. 优先链接现代 CMake target,例如 Eigen3::EigenOpenCV::opencv_core
  4. 不把所有第三方库都堆到顶层 CMakeLists.txt

find_package 基本语法

find_package(<PackageName> [version] [REQUIRED] [COMPONENTS components...])

常见写法:

find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem)
find_package(PCL REQUIRED COMPONENTS common io)

参数说明:

参数作用
<PackageName>包名,例如 Eigen3OpenCV
version要求的最低版本或精确版本
REQUIRED找不到就直接报错
COMPONENTS只查找某些组件

REQUIRED

find_package(Eigen3 REQUIRED)

如果没找到 Eigen,CMake configure 阶段直接失败。

如果不写:

find_package(Eigen3)

则需要自己判断:

if(Eigen3_FOUND)
  target_link_libraries(my_target PUBLIC Eigen3::Eigen)
endif()

工程模板里通常写 REQUIRED,因为依赖缺失时越早报错越好。

COMPONENTS

OpenCV、Boost、PCL 这类库通常有多个模块。

例如:

find_package(OpenCV REQUIRED COMPONENTS core imgproc highgui)

表示只需要:

  1. opencv_core
  2. opencv_imgproc
  3. opencv_highgui

这样比直接引入全部 OpenCV 更清楚。

第三方库链接时也要考虑 PUBLICPRIVATEINTERFACE

关键字什么时候用
PRIVATE第三方库只在 .cpp 里使用,头文件不暴露它
PUBLIC头文件里包含了第三方库类型,使用者也需要知道它
INTERFACE当前 target 自己不编译,只向下游传递

例如,lib1 的头文件如果只是这样:

#pragma once

namespace lib1 {
void run_eigen_vector_example();
}

头文件没有暴露 Eigen 类型,那么 Eigen 可以用 PRIVATE

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    Eigen3::Eigen
)

如果头文件写成:

#pragma once

#include <Eigen/Dense>

namespace lib1 {
Eigen::Vector3d make_vector();
}

此时下游包含这个头文件时也需要 Eigen include 路径,所以应该用 PUBLIC

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
)

本模板示例用 PUBLIC,是为了展示依赖传播;实际项目中可以按头文件是否暴露第三方类型来选择。

Eigen3

Eigen 是常用线性代数库,主要是头文件库。

安装

Ubuntu/Debian:

sudo apt install libeigen3-dev

Fedora:

sudo dnf install eigen3-devel

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(Eigen3 REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
)

C++ 使用

#include <Eigen/Dense>

Eigen::Vector3d v(1.0, 2.0, 3.0);

可见性建议

场景链接方式
Eigen 只在 .cpp 中使用PRIVATE Eigen3::Eigen
头文件暴露 Eigen::MatrixEigen::VectorPUBLIC Eigen3::Eigen

OpenCV4

OpenCV 是计算机视觉库,模块很多,建议按组件引入。

安装

Ubuntu/Debian:

sudo apt install libopencv-dev

Fedora:

sudo dnf install opencv-devel

常用组件

组件作用
core基础数据结构,例如 cv::Mat
imgproc图像处理
imgcodecs读写图片
highgui简单窗口显示
videoio摄像头和视频读写
calib3d相机标定、几何
features2d特征点
dnnDNN 推理模块

CMake 写法:推荐按组件

# ========================
# Third-party dependencies
# ========================
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs highgui)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    opencv_core
    opencv_imgproc
    opencv_imgcodecs
    opencv_highgui
)

有些 OpenCV 安装会提供 OpenCV::opencv_core 这种 imported target,也可以写:

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    OpenCV::opencv_core
    OpenCV::opencv_imgproc
    OpenCV::opencv_imgcodecs
    OpenCV::opencv_highgui
)

如果你的系统没有这些 OpenCV:: target,就使用前一种 opencv_core 写法。

CMake 写法:简单粗暴版

find_package(OpenCV REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    ${OpenCV_LIBS}
)

这种写法能用,但不如组件写法清楚。模板教学推荐优先按组件写。

C++ 使用

#include <opencv2/opencv.hpp>

cv::Mat image = cv::imread("test.png");

Boost

Boost 是大型 C++ 库集合。这里以 systemfilesystem 为例。

安装

Ubuntu/Debian:

sudo apt install libboost-all-dev

Fedora:

sudo dnf install boost-devel

如果只想安装少量组件,Ubuntu/Debian 也可以按需安装类似:

sudo apt install libboost-system-dev libboost-filesystem-dev

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(Boost REQUIRED COMPONENTS system filesystem)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    Boost::system
    Boost::filesystem
)

C++ 使用

#include <boost/filesystem.hpp>

boost::filesystem::path p{"."};

注意:如果使用 C++17 的 std::filesystem,很多场景已经不需要 Boost.Filesystem。

PCL

PCL 是点云库,机器人、三维感知、SLAM 项目中常用。

安装

Ubuntu/Debian:

sudo apt install libpcl-dev

Fedora:

sudo dnf install pcl-devel

常用组件

组件作用
common点类型、基础工具
ioPCD/PLY 等文件读写
filters滤波
features特征
registration配准
segmentation分割
visualization可视化

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(PCL REQUIRED COMPONENTS common io filters)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    ${PCL_LIBRARIES}
)

target_include_directories(${PREFIX}_src_lib
  PRIVATE
    ${PCL_INCLUDE_DIRS}
)

target_compile_definitions(${PREFIX}_src_lib
  PRIVATE
    ${PCL_DEFINITIONS}
)

有些 PCL 版本也提供 imported targets。如果你的环境支持,可以优先使用类似:

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    PCL::common
    PCL::io
    PCL::filters
)

实际选择以你本机 find_package(PCL ...) 提供的结果为准。

fmt

fmt 是格式化输出库,C++20 std::format 的风格也来自它。

安装

Ubuntu/Debian:

sudo apt install libfmt-dev

Fedora:

sudo dnf install fmt-devel

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(fmt REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    fmt::fmt
)

C++ 使用

#include <fmt/core.h>

auto s = fmt::format("value = {}", 42);

spdlog

spdlog 是常用日志库。

安装

Ubuntu/Debian:

sudo apt install libspdlog-dev

Fedora:

sudo dnf install spdlog-devel

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(spdlog REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    spdlog::spdlog
)

C++ 使用

#include <spdlog/spdlog.h>

spdlog::info("hello {}", "spdlog");

yaml-cpp

yaml-cpp 常用于读取配置文件。

安装

Ubuntu/Debian:

sudo apt install libyaml-cpp-dev

Fedora:

sudo dnf install yaml-cpp-devel

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(yaml-cpp REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    yaml-cpp::yaml-cpp
)

有些旧环境可能 target 名是 yaml-cpp,如果 yaml-cpp::yaml-cpp 不存在,再根据报错调整。

nlohmann_json

nlohmann_json 是常用 JSON 库,通常是头文件库。

安装

Ubuntu/Debian:

sudo apt install nlohmann-json3-dev

Fedora:

sudo dnf install json-devel

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(nlohmann_json REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    nlohmann_json::nlohmann_json
)

C++ 使用

#include <nlohmann/json.hpp>

nlohmann::json data;
data["name"] = "cmake_template";

Threads

C++ 标准线程库通常不需要安装额外包,但链接时建议用 CMake 的 Threads 包。

安装

一般不需要单独安装。

如果缺少编译器工具链:

Ubuntu/Debian:

sudo apt install build-essential

Fedora:

sudo dnf install gcc gcc-c++ make

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(Threads REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    Threads::Threads
)

C++ 使用

#include <thread>

std::thread worker([] {
    // do work
});
worker.join();

OpenMP

OpenMP 用于多线程并行计算。

安装

Ubuntu/Debian:

sudo apt install libomp-dev

Fedora:

sudo dnf install libgomp

CMake 写法

# ========================
# Third-party dependencies
# ========================
find_package(OpenMP REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    OpenMP::OpenMP_CXX
)

C++ 使用

#include <omp.h>

#pragma omp parallel for
for (int i = 0; i < 100; ++i) {
    // parallel work
}

Sophus

Sophus 是李群李代数库,在 SLAM、机器人位姿计算中常用。

安装

很多发行版仓库不一定提供合适版本,常见做法是从源码安装,或者由 ROS / 项目依赖管理。

如果系统仓库提供,可以尝试搜索:

apt search sophus
dnf search sophus

CMake 写法

如果已经安装并提供 CMake config:

# ========================
# Third-party dependencies
# ========================
find_package(Sophus REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    Sophus::Sophus
)

Sophus 常依赖 Eigen。如果你的代码同时直接使用 Eigen,也可以显式写:

find_package(Eigen3 REQUIRED)
find_package(Sophus REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
  PRIVATE
    Sophus::Sophus
)

常用依赖速查表

Ubuntu/DebianFedoraCMake 查找常用链接目标
Eigen3libeigen3-deveigen3-develfind_package(Eigen3 REQUIRED)Eigen3::Eigen
OpenCVlibopencv-devopencv-develfind_package(OpenCV REQUIRED COMPONENTS ...)opencv_core
Boostlibboost-all-devboost-develfind_package(Boost REQUIRED COMPONENTS ...)Boost::system
PCLlibpcl-devpcl-develfind_package(PCL REQUIRED COMPONENTS ...)${PCL_LIBRARIES}PCL::common
fmtlibfmt-devfmt-develfind_package(fmt REQUIRED)fmt::fmt
spdloglibspdlog-devspdlog-develfind_package(spdlog REQUIRED)spdlog::spdlog
yaml-cpplibyaml-cpp-devyaml-cpp-develfind_package(yaml-cpp REQUIRED)yaml-cpp::yaml-cpp
nlohmann_jsonnlohmann-json3-devjson-develfind_package(nlohmann_json REQUIRED)nlohmann_json::nlohmann_json
Threads通常无需单独安装通常无需单独安装find_package(Threads REQUIRED)Threads::Threads
OpenMPlibomp-devlibgompfind_package(OpenMP REQUIRED)OpenMP::OpenMP_CXX

不同发行版和版本的包名可能略有差异。如果安装失败,先用:

apt search <关键>
dnf search <关键>

确认包名。

一个模块同时引入 Eigen 和 OpenCV

例如 lib1 同时使用 Eigen 和 OpenCV:

# ========================
# Third-party dependencies
# ========================
find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
  PRIVATE
    opencv_core
    opencv_imgproc
    opencv_imgcodecs
)

如果头文件不暴露 Eigen 类型,也可以把 Eigen 改成 PRIVATE

target_link_libraries(${PREFIX}_src_lib
  PRIVATE
    Eigen3::Eigen
    opencv_core
    opencv_imgproc
    opencv_imgcodecs
)

不推荐的写法

不推荐:顶层集中查找所有库

# 顶层 CMakeLists.txt
find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED)
find_package(PCL REQUIRED)

问题:

  1. 顶层越来越乱。
  2. 不知道哪个模块真正用了哪个库。
  3. 删除模块时容易漏删依赖。
  4. 新人阅读工程时依赖关系不清楚。

不推荐:全局 include

include_directories(${OpenCV_INCLUDE_DIRS})
link_libraries(${OpenCV_LIBS})

问题:

  1. 影响所有 target。
  2. 依赖关系不明确。
  3. 可能引入意外的 include 顺序问题。

现代 CMake 推荐:

target_link_libraries(my_target
  PRIVATE
    opencv_core
)

如果库提供 imported target,include 路径和链接库会自动跟着 target 传播。

排查 find_package 找不到库

第一步:确认系统包是否安装

dpkg -l | grep eigen
rpm -qa | grep eigen

第二步:确认 CMake 能看到 config 文件

常见文件名:

Eigen3Config.cmake
OpenCVConfig.cmake
PCLConfig.cmake
fmtConfig.cmake

第三步:手动指定搜索路径

如果库安装在非标准目录,可以在 configure 时添加:

cmake --preset linux-debug -DCMAKE_PREFIX_PATH=/opt/some_library

或在 preset 中添加:

"CMAKE_PREFIX_PATH": "/opt/some_library"

多个路径可以用分号:

"CMAKE_PREFIX_PATH": "/opt/lib_a;/opt/lib_b"

第四步:查看 CMake 报错信息

CMake 通常会告诉你缺少哪个 config 文件,例如:

Could not find a package configuration file provided by "OpenCV"

这种情况优先检查:

  1. 开发包是否安装。
  2. 包名是否正确。
  3. 是否安装到了非标准路径。
音乐页