跳到正文

Wiki

第三方庫依賴寫法

約 9 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

本節專門講第三方庫如何安裝、如何在 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

1.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 只查找某些元件

1.1.REQUIRED

find_package(Eigen3 REQUIRED)

如果沒找到 Eigen,CMake configure 階段直接失敗。

如果不寫:

find_package(Eigen3)

則需要自己判斷:

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

工程模板裡通常寫 REQUIRED,因為依賴缺失時越早報錯越好。

1.2.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,是為了展示依賴傳播;實際專案中可以按標頭檔案是否暴露第三方型別來選擇。

3.Eigen3

Eigen 是常用線性代數庫,主要是標頭檔案庫。

3.1.安裝

Ubuntu/Debian:

sudo apt install libeigen3-dev

Fedora:

sudo dnf install eigen3-devel

3.2.CMake 寫法

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

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

3.3.C++ 使用

#include <Eigen/Dense>

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

3.4.可見性建議

場景 連結方式
Eigen 只在 .cpp 中使用 PRIVATE Eigen3::Eigen
標頭檔案暴露 Eigen::MatrixEigen::Vector PUBLIC Eigen3::Eigen

4.OpenCV4

OpenCV 是計算機視覺庫,模組很多,建議按元件引入。

4.1.安裝

Ubuntu/Debian:

sudo apt install libopencv-dev

Fedora:

sudo dnf install opencv-devel

4.2.常用元件

元件 作用
core 基礎資料結構,例如 cv::Mat
imgproc 影像處理
imgcodecs 讀寫圖片
highgui 簡單視窗顯示
videoio 攝像頭和影片讀寫
calib3d 相機標定、幾何
features2d 特徵點
dnn DNN 推理模組

4.3.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 寫法。

4.4.CMake 寫法:簡單粗暴版

find_package(OpenCV REQUIRED)

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

這種寫法能用,但不如元件寫法清楚。模板教學推薦優先按元件寫。

4.5.C++ 使用

#include <opencv2/opencv.hpp>

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

5.Boost

Boost 是大型 C++ 庫集合。這裡以 systemfilesystem 為例。

5.1.安裝

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

5.2.CMake 寫法

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

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

5.3.C++ 使用

#include <boost/filesystem.hpp>

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

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

6.PCL

PCL 是點雲庫,機器人、三維感知、SLAM 專案中常用。

6.1.安裝

Ubuntu/Debian:

sudo apt install libpcl-dev

Fedora:

sudo dnf install pcl-devel

6.2.常用元件

元件 作用
common 點型別、基礎工具
io PCD/PLY 等檔案讀寫
filters 濾波
features 特徵
registration 配準
segmentation 分割
visualization 視覺化

6.3.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 ...) 提供的結果為準。

7.fmt

fmt 是格式化輸出庫,C++20 std::format 的風格也來自它。

7.1.安裝

Ubuntu/Debian:

sudo apt install libfmt-dev

Fedora:

sudo dnf install fmt-devel

7.2.CMake 寫法

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

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

7.3.C++ 使用

#include <fmt/core.h>

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

8.spdlog

spdlog 是常用日誌庫。

8.1.安裝

Ubuntu/Debian:

sudo apt install libspdlog-dev

Fedora:

sudo dnf install spdlog-devel

8.2.CMake 寫法

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

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

8.3.C++ 使用

#include <spdlog/spdlog.h>

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

9.yaml-cpp

yaml-cpp 常用於讀取配置檔案。

9.1.安裝

Ubuntu/Debian:

sudo apt install libyaml-cpp-dev

Fedora:

sudo dnf install yaml-cpp-devel

9.2.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 不存在,再根據報錯調整。

10.nlohmann_json

nlohmann_json 是常用 JSON 庫,通常是標頭檔案庫。

10.1.安裝

Ubuntu/Debian:

sudo apt install nlohmann-json3-dev

Fedora:

sudo dnf install json-devel

10.2.CMake 寫法

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

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

10.3.C++ 使用

#include <nlohmann/json.hpp>

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

11.Threads

C++ 標準執行緒庫通常不需要安裝額外包,但連結時建議用 CMake 的 Threads 包。

11.1.安裝

一般不需要單獨安裝。

如果缺少編譯器工具鏈:

Ubuntu/Debian:

sudo apt install build-essential

Fedora:

sudo dnf install gcc gcc-c++ make

11.2.CMake 寫法

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

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

11.3.C++ 使用

#include <thread>

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

12.OpenMP

OpenMP 用於多執行緒平行計算。

12.1.安裝

Ubuntu/Debian:

sudo apt install libomp-dev

Fedora:

sudo dnf install libgomp

12.2.CMake 寫法

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

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

12.3.C++ 使用

#include <omp.h>

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

13.Sophus

Sophus 是李群李代數庫,在 SLAM、機器人位姿計算中常用。

13.1.安裝

很多發行版倉庫不一定提供合適版本,常見做法是從原始碼安裝,或者由 ROS / 專案依賴管理。

如果系統倉庫提供,可以嘗試搜尋:

apt search sophus
dnf search sophus

13.2.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
)

14.常用依賴速查表

Ubuntu/Debian Fedora CMake 查詢 常用連結目標
Eigen3 libeigen3-dev eigen3-devel find_package(Eigen3 REQUIRED) Eigen3::Eigen
OpenCV libopencv-dev opencv-devel find_package(OpenCV REQUIRED COMPONENTS ...) opencv_core
Boost libboost-all-dev boost-devel find_package(Boost REQUIRED COMPONENTS ...) Boost::system
PCL libpcl-dev pcl-devel find_package(PCL REQUIRED COMPONENTS ...) ${PCL_LIBRARIES}PCL::common
fmt libfmt-dev fmt-devel find_package(fmt REQUIRED) fmt::fmt
spdlog libspdlog-dev spdlog-devel find_package(spdlog REQUIRED) spdlog::spdlog
yaml-cpp libyaml-cpp-dev yaml-cpp-devel find_package(yaml-cpp REQUIRED) yaml-cpp::yaml-cpp
nlohmann_json nlohmann-json3-dev json-devel find_package(nlohmann_json REQUIRED) nlohmann_json::nlohmann_json
Threads 通常無需單獨安裝 通常無需單獨安裝 find_package(Threads REQUIRED) Threads::Threads
OpenMP libomp-dev libgomp find_package(OpenMP REQUIRED) OpenMP::OpenMP_CXX

不同發行版和版本的包名可能略有差異。如果安裝失敗,先用:

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

確認包名。

15.一個模組同時引入 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
)

16.不推薦的寫法

16.1.不推薦:頂層集中查詢所有庫

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

問題:

  1. 頂層越來越亂。
  2. 不知道哪個模組真正用了哪個庫。
  3. 刪除模組時容易漏刪依賴。
  4. 新人閱讀工程時依賴關係不清楚。

16.2.不推薦:全域性 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 傳播。

17.排查 find_package 找不到庫

17.1.第一步:確認系統包是否安裝

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

17.2.第二步:確認 CMake 能看到 config 檔案

常見檔名:

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

17.3.第三步:手動指定搜尋路徑

如果庫安裝在非標準目錄,可以在 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"

17.4.第四步:檢視 CMake 報錯資訊

CMake 通常會告訴你缺少哪個 config 檔案,例如:

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

這種情況優先檢查:

  1. 開發包是否安裝。
  2. 包名是否正確。
  3. 是否安裝到了非標準路徑。