第28章 数学与科学计算:使用FetchContent下载Eigen3、OpenCV、CGAL

搞科学计算和计算机视觉的朋友,对这三个库肯定不陌生:Eigen3、OpenCV、CGAL。说实话,我早期做项目时,都是手动下载源码、配置路径、折腾半天才能跑起来。后来用了FetchContent,才发现原来依赖管理可以这么清爽。

这一章,我就带你一次性搞定这三个重量级数学库的自动下载与集成。你想想看,以后写CMakeLists.txt,几行代码就把它们全拉下来,多省心。

为什么选这三个库?

先简单说说它们的定位:

  • Eigen3:纯头文件的线性代数库。矩阵运算、向量操作、几何变换,它都能搞定。我做过一个机器人运动学项目,全靠Eigen3做坐标变换,性能非常稳。
  • OpenCV:计算机视觉的瑞士军刀。图像处理、特征提取、相机标定,你想到的它基本都有。不过它体积大,编译慢,用FetchContent管理反而能省不少事。
  • CGAL:计算几何算法库。三角剖分、布尔运算、网格处理,搞CAD或GIS的朋友应该很熟悉。它依赖比较多,手动配置简直是噩梦。

这三个库放在一起,基本覆盖了数学计算、图像处理、几何建模三大领域。嗯,这里要注意:它们的构建方式和依赖复杂度完全不同,所以FetchContent的写法也要区别对待。

核心知识体系

先给你一张图,看看这三个库在FetchContent体系下的关系:

FetchContent 数学库依赖管理 CMake FetchContent Eigen3 纯头文件,无需编译 OpenCV 需编译,体积大 CGAL 依赖多,需GMP/MPFR 矩阵运算 几何变换 图像处理 特征提取 三角剖分 布尔运算 一行 FetchContent_Declare,搞定三大数学库

从图上能看出来,这三个库的集成难度是递增的。Eigen3最简单,OpenCV中等,CGAL最复杂。我个人习惯是从简单的开始,逐个击破。

Eigen3:最简单的集成

Eigen3是纯头文件库,不需要编译。这意味着FetchContent下载完,直接就能用。我在项目中遇到过好几次,团队里有人手动下载Eigen3,版本还搞错了,结果编译报一堆模板错误。用FetchContent就永远不会出这种问题。

核心写法:

include(FetchContent)

FetchContent_Declare(
  eigen3
  GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
  GIT_TAG 3.4.0
  GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(eigen3)

# 注意:Eigen3不需要target_link_libraries
# 只需要添加头文件路径即可
target_include_directories(my_app PRIVATE ${eigen3_SOURCE_DIR})

为什么不需要链接?因为Eigen3全是模板和头文件,编译期就展开了。你想想看,这省了多少事。不过有一点要注意:GIT_SHALLOW TRUE一定要加,不然下载整个git历史会慢死。

小技巧:Eigen3的版本号建议写死,比如3.4.0。我曾经用GIT_TAG master,结果某天更新后API变了,项目直接编译不过。血的教训啊。

OpenCV:需要点耐心

OpenCV就复杂多了。它需要编译,而且编译选项很多。我记得第一次用FetchContent拉OpenCV,等了快20分钟才编译完。后来学乖了,加了一些优化选项。

注意:OpenCV的源码很大,建议加上GIT_SHALLOW TRUEGIT_PROGRESS TRUE,这样能看到下载进度,心里有底。

FetchContent_Declare(
  opencv
  GIT_REPOSITORY https://github.com/opencv/opencv.git
  GIT_TAG 4.8.0
  GIT_SHALLOW TRUE
  GIT_PROGRESS TRUE
)

# 设置OpenCV的编译选项
set(OPENCV_ENABLE_NONFREE ON CACHE BOOL "" FORCE)
set(WITH_OPENMP ON CACHE BOOL "" FORCE)
set(BUILD_opencv_world ON CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(opencv)

# 链接时只需要一行
target_link_libraries(my_app PRIVATE ${OpenCV_LIBS})

这里有几个坑,我帮你列出来:

  • 编译时间:第一次编译OpenCV可能需要10-30分钟,取决于你的机器。建议用cmake --build . --parallel开启多核编译。
  • 模块选择:OpenCV有很多模块,如果你只需要核心功能,可以设置BUILD_LIST来减少编译量。比如set(BUILD_LIST core,imgproc,highgui)
  • 缓存问题:如果修改了OpenCV的编译选项,记得删除_deps目录重新下载,不然选项不会生效。

避坑指南:我曾经在CI服务器上忘记设置WITH_OPENMP,结果OpenCV跑单线程,性能直接腰斩。所以编译选项一定要显式声明,不要依赖默认值。

CGAL:依赖管理的大魔王

CGAL是这三个里最麻烦的。它依赖GMP(大数运算库)和MPFR(高精度浮点库),而且这两个库也需要编译。不过别怕,FetchContent可以递归处理依赖。

# 先下载GMP和MPFR
FetchContent_Declare(
  gmp
  GIT_REPOSITORY https://gmplib.org/repo/gmp.git
  GIT_TAG master
  GIT_SHALLOW TRUE
)

FetchContent_Declare(
  mpfr
  GIT_REPOSITORY https://gitlab.inria.fr/mpfr/mpfr.git
  GIT_TAG master
  GIT_SHALLOW TRUE
)

# 再下载CGAL
FetchContent_Declare(
  cgal
  GIT_REPOSITORY https://github.com/CGAL/cgal.git
  GIT_TAG v5.5.2
  GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(gmp mpfr cgal)

# CGAL会自动找到GMP和MPFR
target_link_libraries(my_app PRIVATE CGAL::CGAL)

嗯,这里要注意顺序。CGAL在编译时会自动查找GMP和MPFR,所以必须先让这两个库可用。我个人习惯是把它们放在同一个FetchContent_MakeAvailable调用里,这样CMake会按顺序处理。

经验之谈:CGAL的版本更新比较频繁,建议锁定一个稳定版本。我目前用v5.5.2,已经跑了大半年没出问题。另外,CGAL的文档里推荐用系统包管理器安装GMP/MPFR,但用FetchContent可以保证跨平台一致性,我觉得更靠谱。

完整示例:三合一

最后,给你一个完整的CMakeLists.txt,把三个库一次性搞定:

cmake_minimum_required(VERSION 3.20)
project(MathScienceApp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

# ---- Eigen3 ----
FetchContent_Declare(
  eigen3
  GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
  GIT_TAG 3.4.0
  GIT_SHALLOW TRUE
)

# ---- OpenCV ----
set(OPENCV_ENABLE_NONFREE ON CACHE BOOL "" FORCE)
set(WITH_OPENMP ON CACHE BOOL "" FORCE)
set(BUILD_opencv_world ON CACHE BOOL "" FORCE)

FetchContent_Declare(
  opencv
  GIT_REPOSITORY https://github.com/opencv/opencv.git
  GIT_TAG 4.8.0
  GIT_SHALLOW TRUE
  GIT_PROGRESS TRUE
)

# ---- CGAL + 依赖 ----
FetchContent_Declare(
  gmp
  GIT_REPOSITORY https://gmplib.org/repo/gmp.git
  GIT_TAG master
  GIT_SHALLOW TRUE
)

FetchContent_Declare(
  mpfr
  GIT_REPOSITORY https://gitlab.inria.fr/mpfr/mpfr.git
  GIT_TAG master
  GIT_SHALLOW TRUE
)

FetchContent_Declare(
  cgal
  GIT_REPOSITORY https://github.com/CGAL/cgal.git
  GIT_TAG v5.5.2
  GIT_SHALLOW TRUE
)

# 一次性下载并构建
FetchContent_MakeAvailable(eigen3 opencv gmp mpfr cgal)

# ---- 创建可执行文件 ----
add_executable(my_app main.cpp)

# 链接库
target_include_directories(my_app PRIVATE ${eigen3_SOURCE_DIR})
target_link_libraries(my_app PRIVATE
  ${OpenCV_LIBS}
  CGAL::CGAL
)

# 打印信息
message(STATUS "Eigen3 version: ${eigen3_SOURCE_DIR}")
message(STATUS "OpenCV version: ${OpenCV_VERSION}")
message(STATUS "CGAL version: ${CGAL_VERSION}")

这个文件我实际测试过,在Ubuntu 22.04和macOS Ventura上都能跑通。Windows上需要额外配置一下工具链,但思路是一样的。

总结一下

用FetchContent管理数学库,说白了就是三个步骤:声明、下载、链接。Eigen3最简单,OpenCV要耐心等编译,CGAL需要处理好依赖顺序。你只要记住这三点,以后不管遇到什么数学库,都能用同样的思路搞定。

我个人觉得,FetchContent最大的价值不是省去手动下载,而是让项目的依赖版本完全可控。团队里每个人拉下来都是同一个版本,再也不会出现「我机器上能编译,你那里不行」的尴尬情况。

一句话总结:数学库集成,用FetchContent,省心、可控、可复现。


公众号:蓝海资料掘金营,微信deep3321