安装
获取源代码
你可以先从最新稳定发行版开始。
或者,如果你想获得最新版本,可以 clone 下面这个 git 目录:
git clone https://ceres-solver.googlesource.com/ceres-solver
依赖项
Ceres 依赖一些开源库,其中一部分是可选项。关于自定义编译选项的细节,参考自定义编译部分。
Eigen 3.2.2 或以上版本(强烈建议),3.1.0 或以上版本(最低要求)。
⚠️ 注意
Ceres 也使用 Eigen 作为稀疏线性代数运算库。参看 EIGENSPARSE 相关文档。
CMake 2.8.0 或以上版本。除 Android 外所有平台都要求。
glog 0.3.1 或以上版本。推荐。
glog
广泛应用于 Ceres 中,用于记录内存分配、求解过程各个部分的耗时、内部错误条件等详细信息。Ceres 的开发者频繁地用其监测和分析 Ceres 的运行表现。glog 允许你通过命令行来控制其行为。由-logtostderr
指令开始,你可以加入-v=N
来提高N
的值,以获取关于 Ceres 内部的更多细节。不幸的是,当前版本的 google-glog 无法用 Android NDK 编译。所以,Ceres 同时提供
glog
的一个小型替代品miniglog
,可以在编译选项中加入MINIGLOG
来启动。为了减少依赖项,在 Android 之外的平台使用 miniglog 似乎也很诱人。虽然没人阻止用户这么做,但我们强烈不建议,因为
miniglog
的性能、可控性、可用性都比glog
差。⚠️ 注意
如果你使用源代码来编译
glog
,需要留意目前glog
的单元测试部分(默认开启)无法直接基于gflags
2.1 编译,这是因为 gflags 的命名空间从google::
改成了gflags::
。你可以从此处下载一个补丁来解决。gflags,编译示例和测试案例所需。
SuiteSparse,求解大型稀疏线性系统所需。可选项;对于大型 bundle adjustment,强烈推荐。
CXSparese。和 SuiteSparse 相似,但更简单,速度较慢。CXSparse 不依赖
LAPACK
和BLAS
,可以简化编译流程,生成更小的二进制文件。可选项。BLAS 和 LAPACK,为
SuiteSparse
所依赖。可选项,Ceres 中的部分操作所需。对于 Mac OS X 之外的其他类 UNIX 系统使用者,我们推荐 ATLAS,它内置了
BLAS
和LAPACK
。使用 OpenBLAS 也是可行的,但注意要关掉 OpenBLAS 内部的多线程功能,因其与 Ceres 本身的多线程冲突。Mac OS X 在
Accelerate
框架内提供了经过优化的LAPACK
和BLAS
,能被 Ceres 的编译系统自动检测和使用。Windows 上的情况复杂得多。LAPACK For Windows 有详细指导。
BLAS
和LAPACK
为可选项,但为SuiteSparse
所必需。
Linux
我们的演示使用 Ubuntu 这一发行版。
⚠️ 注意
至少在 Ubuntu 14.04 之前,官方软件包仓库中的 SuiteSparse 包(基于 SuiteSparse v3.4.0 构建)不能作为共享库用于 Ceres 的编译。因此,如果要使用 SuiteSparse 作为共享库来编译 Ceres,必须通过源安装或者外部 PPA 来安装 SuiteSparse(参看此处 bug report)。 建议使用最新版本的 SuiteSparse (在撰写本文时为 4.2.1)。
首先,安装所有依赖项:
# CMake
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev
# BLAS & LAPACK
sudo apt-get install libatlas-base-dev
# Eigen3
sudo apt-get install libeigen3-dev
# SuiteSparse and CXSparse (optional)
# - If you want to build Ceres as a *static* library (the default)
# you can use the SuiteSparse package in the main Ubuntu package
# repository:
sudo apt-get install libsuitesparse-dev
# - However, if you want to build Ceres as a *shared* library, you must
# add the following PPA:
sudo add-apt-repository ppa:bzindovic/suitesparse-bugfix-1319687
sudo apt-get update
sudo apt-get install libsuitesparse-dev
现在可以编译、测试和安装 Ceres:
tar zxf ceres-solver-1.13.0.tar.gz
mkdir ceres-bin
cd ceres-bin
cmake ../ceres-solver-1.13.0
make -j3
make test
# Optionally install Ceres, it can also be exported using CMake which
# allows Ceres to be used without requiring installation, see the documentation
# for the EXPORT_BUILD_DIR option for more information.
make install
你可以顺便跑一下内置示例中的 bundle adjustment 应用(来自华盛顿大学 BAL 数据集 [Agarwal]):
bin/simple_bundle_adjuster ../ceres-solver-1.13.0/data/problem-16-22106-pre.txt
该程序使用 DENSE_SCHUR
线性求解器,最高迭代次数为 10 次。预期输出如下:
ter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 4.185660e+06 0.00e+00 1.09e+08 0.00e+00 0.00e+00 1.00e+04 0 7.59e-02 3.37e-01
1 1.062590e+05 4.08e+06 8.99e+06 5.36e+02 9.82e-01 3.00e+04 1 1.65e-01 5.03e-01
2 4.992817e+04 5.63e+04 8.32e+06 3.19e+02 6.52e-01 3.09e+04 1 1.45e-01 6.48e-01
3 1.899774e+04 3.09e+04 1.60e+06 1.24e+02 9.77e-01 9.26e+04 1 1.43e-01 7.92e-01
4 1.808729e+04 9.10e+02 3.97e+05 6.39e+01 9.51e-01 2.78e+05 1 1.45e-01 9.36e-01
5 1.803399e+04 5.33e+01 1.48e+04 1.23e+01 9.99e-01 8.33e+05 1 1.45e-01 1.08e+00
6 1.803390e+04 9.02e-02 6.35e+01 8.00e-01 1.00e+00 2.50e+06 1 1.50e-01 1.23e+00
Ceres Solver v1.13.0 Solve Report
----------------------------------
Original Reduced
Parameter blocks 22122 22122
Parameters 66462 66462
Residual blocks 83718 83718
Residual 167436 167436
Minimizer TRUST_REGION
Dense linear algebra library EIGEN
Trust region strategy LEVENBERG_MARQUARDT
Given Used
Linear solver DENSE_SCHUR DENSE_SCHUR
Threads 1 1
Linear solver threads 1 1
Linear solver ordering AUTOMATIC 22106, 16
Cost:
Initial 4.185660e+06
Final 1.803390e+04
Change 4.167626e+06
Minimizer iterations 6
Successful steps 6
Unsuccessful steps 0
Time (in seconds):
Preprocessor 0.261
Residual evaluation 0.082
Jacobian evaluation 0.412
Linear solver 0.442
Minimizer 1.051
Postprocessor 0.002
Total 1.357
Termination: CONVERGENCE (Function tolerance reached. |cost_change|/cost: 1.769766e-09 <= 1.000000e-06)
Mac OS X
⚠️ 注意
Ceres 无法使用 Xcode 4.5.x (Clang 4.1)编译(因为该 Clang 版本中存在一个 bug)。如果你使用 Xcode 4.5.x,请在编译 Ceres 前升级到 Xcode 4.6.x 或以上版本。
在 Mac OS X 上,你可以使用 MacPort 或 HomeBrew 来安装 Ceres。
如果使用 MacPort,则可如下安装最新版本:
sudo port install ceres-solver
如果使用 HomeBrew,并已启动 homebrew/science
选项[1],则可如下安装最新稳定版本及所有依赖项:
brew install ceres-solver
或者如下安装 git 仓库中的最新版本:
brew install ceres-solver --HEAD
你也可以用 HomeBrew 逐项安装依赖项。无需单独安装 BLAS
或者 LAPACK
,因为 OS X 的 vecLib 框架提供了优化版的 BLAS
和 LAPACK
。
# CMake
brew install cmake
# google-glog and gflags
brew install glog
# Eigen3
brew install eigen
# SuiteSparse and CXSparse
brew install suite-sparse
现在可以编译、测试和安装 Ceres:
tar zxf ceres-solver-1.13.0.tar.gz
mkdir ceres-bin
cd ceres-bin
cmake ../ceres-solver-1.13.0
make -j3
make test
# Optionally install Ceres, it can also be exported using CMake which
# allows Ceres to be used without requiring installation, see the
# documentation for the EXPORT_BUILD_DIR option for more information.
make install
Windows
⚠️ 注意
如果你觉得下面的 CMake 流程太繁琐,可能 Tal Ben-Nun 对 Ceres Solver 的 Microsoft Visual Studio 封装 会让你感兴趣。
在 Windows 上,我们支持 Visual Studio 2010 及以上版本。注意,Ceres 的 Windows 版本相对 Linux 或 Mac OS X 版本特性较少且测试较不充分,因为 Windows 平台缺乏 SuiteSparse 和 CXSparse 的官方编译支持。不过,也有非官方支持的编译这些库的方法。同时,Windows 上的编译过程也更复杂,因为没有自动化安装依赖项的方法。
⚠️ 注意
配合 windows.h 使用
google-glog
和miniglog
的注意事项。启用 GDI(Graphics Device Interface)的 windows.h 头文件定义了
ERROR
,这与google-glog
和miniglog
中 LogSeverity 层定义的ERROR
冲突。至少有两个可能的修复方法:
- 在某处建立一个用于存放依赖项、编译文件和源文件的顶层目录:
ceres
- 获取依赖项;解压为顶层目录
ceres
的子目录(如ceres/eigen
,ceres/glog
)Eigen
3.1 (为 Windows 上必需;3.0.x 版本不适用)。无需编译,直接解压即可。google-glog
:打开 Visual Studio 解决方案并编译。gflags
:打开 Visual Studio 解决方案并编译。- (实验阶段)
SuiteSparse
:以前 Windows 上是无法用 SuiteSparse 的,现在有一个可行的方法是使用 suitesparse-metis-for-windows 项目。如果你想使用 SuiteSparse,按其指示获取和编译。 - (实验阶段)
CXSparse
:以前 Windows 上是无法用 CXSparse 的,现在有几个可行的方法,如 [1] 和 [2]。如果你想使用 CXSparse,按其指示获取和编译。
- 解压 Ceres 压缩包至
ceres
目录下。 - 安装 CMake。
- 新建目录
ceres/ceres-bin
(便于在源文件目录外编译)。 - 执行
CMake
;选择ceres-bin
作为编译目录。 尝试执行
Configure
。暂时不会 Configure 成功,而是显示一堆选项,你需要将以下几项的值设为实际的解压或编译目录:EIGEN_INCLUDE_DIR_HINTS
GLOG_INCLUDE_DIR_HINTS
GLOG_LIBRARY_DIR_HINTS
GFLAGS_INCLUDE_DIR_HINTS
GFLAGS_LIBRARY_DIR_HINTS
- (可选)
SUITESPARSE_INCLUDE_DIR_HINTS
- (可选)
SUITESPARSE_LIBRARY_DIR_HINTS
- (可选)
CXSPARSE_INCLUDE_DIR_HINTS
- (可选)
CXSPARSE_LIBRARY_DIR_HINTS
如果其中有的选项在 CMake GUI 中不可见,就自己新建选项。我们建议使用形如
<NAME>_(INCLUDE/LIBRARY)_DIR_HINTS
的变量名而非直接使用<NAME>_INCLUDE_DIR
或<NAME>_LIBRARY
,以保留所有的有效性检测,并避免手动指定库文件。- 你可能还需要再进行一些设定才能生成一个 MSVC 项目。在完成各项设定之后,尝试 Configure & Generate 直至成功生成项目。
- 打开生成的解决方案,在 MSVC 中编译。
如需跑测试案例,选择生成目标中的 RUN_TESTS
,点击 build 菜单中的 Build RUN_TESTS 。
和 Linux 下一样,现在你可以试着跑一下 bin/simple_bundle_adjuster
。
注意:
- 默认编译类型为 Debug;可以考虑切换为 Release。
- 当前
system_test
无法正常工作。 - CMake 默认将生成的测试二进制文件存放在
ceres-bin/examples/Debug
。 - Windows 上支持的求解器为
DENSE_QR
、DENSE_SCHUR
、CGNR
和ITERATIVE_SCHUR
。 - 我们期待有人能针对上游的 SuiteSparse 做点工作,使其支持 CMake 这样易用的编译系统,从而使我们的 Windows 版本能获得完整的支持。
Android
未译
iOS
未译
自定义编译
通过调整 CMake
的特定选项,可以自定义编译生成过程,减少编译 Ceres 所需的依赖库。这些选项可以在 CMake GUI 里设置,也可以在从命令行启动 CMake 时通过 -D<OPTION>=<ON/OFF>
来设置。通常,如果清楚这些选项的默认设置是什么,那只需设置与默认设置不同的选项。
⚠️ 注意
如果你在执行 CMake 时通过
-D<OPTION>=<ON/OFF>
来设置选项,请牢记这会在每次配置之初强行覆盖 CMake cache 中的<VARIABLE>
项。如果是经命令行启动 CMake curses GUI (通过
ccmake
,如ccmake -D<VARIABLE>=<VALUE> <PATH_TO_SRC>
),则可能引起困惑。此时,即使你在 CMake GUI 中修改了<VARIABLE>
的值,你的修改仍会在每次配置之初被通过-D<VARIABLE>=<VALUE>
传递的值覆盖。因此,不通过
-D
传值给 CMake,而是交互式地在 CMake GUI 中试验和修改选项,是更方便的做法。如果在标准视图下看不到选项,用<t>
切换到高级视图。
Ceres 配置选项
LAPACK [Default: ON]
:如果找到LAPACK
,Ceres 默认编译生成时使用LAPACK
(及BLAS
)。如果将这个选项设为OFF
,则 Ceres 编译生成时将不使用SuiteSparse
,因为后者依赖于LAPACK
。SUITESPARSE [Default: ON]
:如果SuiteSparse
及其所有依赖库都存在,Ceres 默认会链接到SuiteSparse
。如果将这个选项设为OFF
,Ceres 编译生成时将不使用SuiteSparse
。如果要使用SuiteSparse
,LAPACK
必须设为ON
。CXSPARSE [Default: ON]
:如果CXSparse
及其所有依赖库都存在,Ceres 默认会链接到CXSparse
。如果将这个选项设为OFF
,Ceres 编译生成时将不使用CXSparse
。EIGENSPARSE [Default: OFF]
:Ceres 默认编译生成时不启用 Eigen 的稀疏 Cholesky 分解。这是因为,该功能的部分代码是基于LGPL
授权的,而 Eigen 是一个仅有头文件的库,如果把这部分代码包含进来,Ceres 就必须使用LGPL
许可协议了。⚠️ 注意
为了获得更好的性能,请使用 3.2.2 或以上版本的 Eigen。
GFLAGS [Default: ON]
:将这个选项设为OFF
,Ceres 编译生成时将不使用gflags
。这也将导致部分示例代码不被编译。MINIGLOG [Default: OFF]
:Ceres 内置一个精简的glog
实现,可以选择性地用作glog
的替代品,从而避免依赖glog
。将这个选项设为ON
,可以使用这个精简版glog
实现。SCHUR_SPECIALIZATIONS [Default: ON]
:SPARSE_SCHUR
求解器可以带来一点性能提升(10-20%),但是如果你更在乎二进制文件的大小或者编译时间,可以将这个选项设为OFF
,来减少一些模板偏特化代码。OPENMP [Default: ON]
:某些平台,如 Android,并不支持基于OpenMP
的多线程。将这个选项设为OFF
,可以关闭多线程。CXX11 [Default: OFF]
:仅支持非 MSVC 编译器。尽管 Ceres 目前不使用 C++ 11,但仍使用
shared_ptr
(必需)和unordered_map
(如果可用);在正式成为 C++ 11 标准之前的迭代版本 TR1 和 C++0x 里,这两者已经存在。因此,使用 TR1/C++0x 版本的shared_ptr
和unordered_map
,可以基于前 C++ 11 编译器来编译生成 Ceres。注意,当使用 GCC 和 Clang 时,默认基于 TR1/C++0x 版本编译(
CXX11=OFF
),并不要求开启-std=c++11
来编译 Ceres,也不要求任何使用 Ceres 的用户代码开启-std=c++11
。但是,如果用户代码使用了 C++11,就会导致shared_ptr
和unordered_map
版本不对应,造成编译错误。开启
CXX11=ON
选项,会强制 Ceres 在能够使用 C++11 时使用 C++11 版本的shared_ptr
和unordered_map
,这样所有使用 Ceres 的用户代码编译时也必须开启-std=c++11
。如果使用 CMake 2.8.12 及以上版本,基于导出的 Ceres 目标中的 CMake 目标属性,这项配置会在该目标被导入时被自动处理。于是,任何使用了 Ceres 的用户代码会自动开启std=c++11
进行编译。**在 CMake 2.8.12 以下版本中,用户需自己保证使用 Ceres 的任何代码都基于-std=c++11
编译。在 OS X 10.9 及以上版本中,Clang 无需开启
-std=c++11
也会自动使用 C++11 版本的shared_ptr
和unordered_map
,所以这个选项添加与否都不会影响两者的版本;不过开启添加该选项后就会要求用户代码也基于-std=c++11
编译。下表总结了
CXX11
选项的影响:OS | CXX11 | 检测到的版本 | Ceres 和用户代码是否需要
-std=c++11
--- | --- | --- | --- Linux (GCC & Clang) | OFF | tr1 | No Linux (GCC & Clang) | ON | std | Yes OS X 10.9+ | OFF | std | No OS X 10.9+ | ON | std | Yes使用 MSVC 不需要考虑
CXX11
选项,因为 MSVC 默认开启能用的 C++ 新特性,也不存在-std=c++11
之类的命令。不过 MinGW 和 CygWin 还是适用CXX11
的,也支持-std=c++11
。BUILD_SHARED_LIBS [Default: OFF]
:Ceres 默认编译生成为静态库,将这个选项设为ON
可以将 Ceres 编译生成为分享库。EXPORT_BUILD_DIR [Default: OFF]
:Ceres 默认配置为纯安装,必须在安装后才能被用户代码使用。将这个选项设为ON
,可以导出 Ceres 的 build 目录位置至用户本地的 CMake 包注册表中,这样 Ceres 无需安装也能够在用户的项目里被 CMake 的find_package(Ceres)
指令检测到。BUILD_DOCUMENTATION [Default: OFF]
:开启这个选项可以生成文档,这需要 Sphinx 和 sphinx-better-theme 存在于 Python 包目录里。此外,make_ceres_docs
也可以用来进行文档的单独生成。MSVC_USE_STATIC_CRT [Default: OFF]
:只适用于 Windows。Ceres 默认使用 Visual Studio 的默认设置,动态 C-运行时 (CRT) 库。将这个选项设为ON
,可以使用 静态 C-运行时库。LIB_SUFFIX [Default: "64" on non-Debian/Arch based 64-bit Linux, otherwise: ""]
:添加至库安装目录的后缀,位于${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}
。文件系统层次结构标准建议 64 位系统将本地库安装到 lib64 而不是 lib。大多数 Linux 发行版都遵循这个惯例,但是基于 Debian 和 Arch 的发行版却没有。注意,
LIB_SUFFIX
通常唯一合理的值是 “” 和 “64”。虽然 Ceres 默认会自动检测非基于 Debian/Arch 的 Linux 发行版并将
LIB_SUFFIX
默认值设为 “64”,但是调用 CMake 时如果手动用-DLIB_SUFFIX=<VALUE>
指定 LIB_SUFFIX,仍会覆盖默认值。
Ceres 依赖库位置选项
Ceres 使用 CMake 的 find_package 指令,借助 Find<DEPENDENCY_NAME>.cmake
脚本文件来寻找各依赖库,这些脚本或是打包在 Ceres 中(对于大部分依赖库),或是 CMake 的标准组件(对于 LAPACK
和 BLAS
)。对于不同的操作系统,这些脚本会搜索各个“标准”位置,以寻找每个依赖库。但是,特别是在 Windows 上,它们可能找不到库。这种情况下,你就需要手动指定其安装位置。Ceres 附带的 Find<DEPENDENCY_NAME>.cmake
脚本支持用两种方式来执行此操作:
设置提示变量,以优先顺序指定要搜索的目录;另外,对于
Find<DEPENDENCY_NAME>.cmake
的以下搜索目录:<DEPENDENCY_NAME (CAPS)>_INCLUDE_DIR_HINTS
<DEPENDENCY_NAME (CAPS)>_LIBRARY_DIR_HINTS
这些变量无法在 GUI 中显示,需要通过命令行的
-D<VAR>=<VALUE>
CMake 参数来设定。设置显式地指定头文件目录和库文件的变量:
<DEPENDENCY_NAME (CAPS)>_INCLUDE_DIR
<DEPENDENCY_NAME (CAPS)>_LIBRARY
这样会绕过
Find<DEPENDENCY_NAME>.cmake
脚本中的所有搜索操作,但验证操作仍会执行。这些变量可以在 CMake GUI 中设置。如果没找到库,这些变量会在标准视图中可见(不过目前的 Ceres 配置要求其始终可见);在高级视图中,它们始终可见。它们也可以用 CMake 的命令行参数
-D<VAR>=<VALUE>
来直接设定。
待译
CMake 调用 Ceres
在用户端代码里使用 CMake 调用 Ceres,应使用 find_package()
,并在以下方法中二选一:
Ceres 必须使用
make install
安装。如果安装位置不是标准位置(即不在 CMake 的缺省搜索路径里),那它默认无法被 CMake 检测到。参看 本地安装。
Ceres 的 build 目录必须导出,方法是在配置 Ceres 时,需启动
EXPORT_BUILD_DIR
选项。
下面举个例子,在一个单独的项目中编译 examples/helloworld.cc,可以使用如下 CMakeList.txt 文件:
cmake_minimum_required(VERSION 2.8)
project(helloworld)
find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})
# helloworld
add_executable(helloworld helloworld.cc)
target_link_libraries(helloworld ${CERES_LIBRARIES})
无论 Ceres 是经过安装还是导出,如果检测到多个版本,可设置 Ceres_DIR
来选取某个版本。如果 Ceres 是经过安装的,Ceres_DIR
应设为包含了 CeresConfig.cmake
文件的目录地址(如 /usr/local/share/Ceres
)。如果 Ceres 是经过导出的,Ceres_DIR
应设为被导出的那个 build 目录。
指定 Ceres 组件
当你调用 find_package(Ceres)
命令时,可以指定你所需要的特定 Ceres 组件,满足条件后 Ceres 才能被报告为已找到。例如,这允许你指定要求一个含有 SuiteSparse 支持的 Ceres 版本。原理上讲,如果你在调用find_package(Ceres)
命令时没有指定任何组件(默认设置),则任一被检测到的 Ceres 版本都将被报告为已找到,不管它在编译生成时启用了哪些组件。
允许被指定的 Ceres 组件包括:
LAPACK
:Ceres 被编译生成时启用了 LAPACK(LAPACK=ON
)。SuiteSparse
:Ceres 被编译生成时启用了 SuiteSparse(SUITESPARSE=ON
)。CXSparse
:Ceres 被编译生成时启用了 CXSparse(CXSPARSE=ON
)。EigenSparse
:Ceres 被编译生成时启用了 Eigen 的稀疏 Cholesky 分解功能(EIGENSPARSE=ON
)。SparseLinearAlgebraLibrary
:Ceres 被编译生成时启用了至少一个稀疏线性代数库。这等价于SuiteSparse
或CXSparse
或EigenSparse
。SchurSpecializations
:Ceres 被编译生成时启用了舒尔补特性(SCHUR_SPECIALIZATIONS=ON
)。OpenMP
:Ceres 被编译生成时启用了 OpenMP(OPENMP=ON
)。C++11
:Ceres 被编译生成时启用了 C++11(CXX11=ON
)。
使用 find_package() 指令中的 COMPONENTS
参数来指定一个或多个 Ceres 组件,例如:
# Find a version of Ceres compiled with SuiteSparse & EigenSparse support.
#
# NOTE: This will report Ceres as **not** found if the detected version of
# Ceres was not compiled with both SuiteSparse & EigenSparse.
# Remember, if you have multiple versions of Ceres installed, you
# can use Ceres_DIR to specify which should be used.
find_package(Ceres REQUIRED COMPONENTS SuiteSparse EigenSparse)
指定 Ceres 版本
在 CMake 找到 Ceres 之后,还可以额外检查软件包版本号,这可以在 find_package() 调用中指定,如:
find_package(Ceres 1.2.3 REQUIRED)
本地安装
如果通过指定 -DCMAKE_INSTALL_PREFIX ="/ some / where / local"
将 Ceres 安装在非标准路径中,则用户应将 PATHS 选项添加到 find_package() 命令,例如,
find_package(Ceres REQUIRED PATHS "/some/where/local/")
注意,虽然这可以用来安装多个版本的 Ceres, 但是,请尽量使用 EXPORT_BUILD_DIR
选项导出 Ceres,而非本地安装。这是因为,导出的 Ceres 版本会被 CMake 自动检测到,不管其位置在何处。在只想使用一个版本的 Ceres、只是不希望安装到系统目录的时候,尤应如此。
理解 CMake 的包管理系统
虽然写一个完整的 CMake 教程超出了本教程的主题范围,这里还是介绍一些使用 Ceres 时常见的 CMake 相关错误。 有关更详细的 CMake 用法,以下参考资料会很有帮助:
-
介绍了 CMake 的核心特性。
ProjectConfig 教程 和 cmake-packages 文档
介绍了如何写一个
ProjectConfig.cmake
文件,方便你使用 CMake 来安装和导出个人项目,下文将会讨论。同时介绍了这些过程与find_package()
搭配使用时 CMake 实际上是如何处理的。ProjectConfig 教程是较旧的版本,目前用于 Ceres 与旧版本 CMake 的兼容。⚠️ 注意
CMake 中的目标(targets)
所有使用 CMake 构建的库和可执行文件都表示为使用
add_library()
和add_executable()
创建的目标。 目标封装了编译或链接对象的规则和依赖(可以是其他目标)。 这允许 CMake 隐式地管理依赖链。 因此,告诉 CMake 一个库目标就足够了:B 依赖于之前声明的库目标 A,CMake 就知道这意味着 B 也依赖于 A 的所有公共依赖。
当一个像 Ceres 这样的项目使用了 CMake 安装,或者其 build 目录导出到本地的 CMake 包注册表中(参看 用 CMake 安装项目 vs 导出其 build 目录),除了公共头文件和编译出来的库文件,还有一套 CMake 的项目配置文件也会被安装到 <INSTALL_ROOT>/share/Ceres
处(如果安装的是 Ceres 的话),或者生成在 build 目录里(如果 Ceres 的 build 目录被导出)。当调用 find_package 指令时,CMake 会检查几处标准安装位置(包括 Linux/Unix 系统下的 /usr/local
),以及本地的 CMake 包注册表,以寻找指定软件包的 CMake 配置文件 (比如,如果调用 find_package(Ceres)
,就寻找 Ceres 的 CMake 配置文件)。具体而言,CMake 会寻找:
<PROJECT_NAME>Config.cmake
(或<lower_case_project_name>-config.cmake
)由项目开发者编写,并在编译生成项目时配置了特定选项和安装位置。该文件定义了
<PROJECT_NAME>_INCLUDE_DIRS
和<PROJECT_NAME>_LIBRARIES
等 CMake 变量,调用者在导入项目时可以使用。
<PROJECT_NAME>Config.cmake
文件通常还会引用同一安装位置下的另一个文件:
<PROJECT_NAME>Targets.cmake
这是 CMake 在安装过程中自动生成的,定义了用户在 CMake 中调用该项目时会用到的导入目标。
导入目标包含的库信息,与在当前 CMake 项目内部使用 add_library()
声明的 CMake 目标是一样的。不过,导入目标指向的是已经由另一个 CMake 项目编译生成的对象。原则上,一个导入目标包含了编译对象的位置以及所有与之链接的公共依赖关系。任何一个在本地声明的目标都可以依赖于一个导入目标,CMake 会管理好依赖关系链,结果如同导入目标已经由当前项目声明在本地一样。
很重要的一点是,和任何本地声明的 CMake 目标一样,一个导入目标被添加为另一个目标的依赖项时,会依据其名称被标识。
于是,在使用 Ceres 的项目中,CMakeLists.txt 有如下内容:
find_package(Ceres REQUIRED)
message("CERES_LIBRARIES = ${CERES_LIBRARIES}")
输出信息将会是 CERES_LIBRARIES = ceres
。但是,这里 ceres
是 CeresTargets.cmake
被读取(这是 find_package(Ceres REQUIRED)
操作的一部分)时创建的一个导入目标。它并不直接引用编译好的 Ceres 库:libceres.a/so/dylib/lib
。这个区别很重要,因为:取决于其编译生成选项,Ceres 可能会有公共链接依赖,这些依赖封装在导入目标中,且当 Ceres 被 CMake 添加为另一个目标的依赖时,会被自动添加到链接环节中。在这种情况下,只链接到 libceres.a/so/dylib/lib
而不考虑其他这些依赖关系,会导致链接错误。
注意,这些描述同时适用于用 CMake 安装的项目和用 export() 而非 install() 导出 build 目录的项目。当启用 EXPORT_BUILD_DIR
选项时,Ceres 同时支持其 build 目录的安装和导出,参看自定义编译部分。
用 CMake 安装项目 vs 导出其 build 目录
安装一个项目时,编译好的库文件和头文件会从源目录和 build 目录被拷贝到安装位置,用户端代码使用的正是这些在被拷贝在安装位置的文件。当一个项目的 build 目录被导出时,CMake 不会拷贝编译好的库文件和头文件,而是在用户本地的 CMake 包注册表(Linux/Unix 上为 <USER_HOME>/.cmake/packages
)为其创建一个入口,该入口包含了项目的 build 目录的路径,在调用 find_package()
指令时 CMake 会检查到。这样的效果是,任何用户端代码可以直接使用该 build 目录中编译好的库文件和头文件,不要求该项目被安装才可以使用。
安装/导出一个使用 Ceres 的项目
正如在理解 CMake 的包管理系统中所述,CERES_LIBRARIES
变量的内容,就是 Ceres 的导入目标的名称。如果是要安装/导出你个人的使用了 Ceres 的项目,请一定要清楚:
导入目标在导入它们的项目被导出时,不会被(一并)导出。
所以,当一个使用了 Ceres 的项目 Foo
被导出时,它的依赖关系列表在另一个通过 find_package(Foo REQUIRED)
导入 Foo
的项目 Bar
的视野中会包括:ceres
。然而,ceres
作为导入目标,其定义并不会随 Foo
被导出而一并被导出。这样,如果没有额外的步骤,在处理 Bar
时,ceres
不会被定义为导入目标。因此,在处理 Bar
时,CMake 会假定 ceres
只是直接参考向 libceres.a/so/dylib/lib
(即编译好的 Ceres 库),如果其存在于当前搜索路径列表中的话。在这种情况下,不会发生 CMake 错误,但 Bar
无法正确链接,因为它没有 Ceres 所需的公共链接依赖关系,这些依赖关系存储在导入目标的定义中。
这种情况的解决方案是,对于使用了 Ceres 的 Foo
项目,在 FooConfig.cmake
中调用 find_package(Ceres)
,这样在 CMake 处理 Bar
时 ceres
会被定义为导入目标。下面是一个 FooConfig.cmake
所需的修改示例:
# Importing Ceres in FooConfig.cmake using CMake 2.8.x style.
#
# When configure_file() is used to generate FooConfig.cmake from
# FooConfig.cmake.in, @Ceres_DIR@ will be replaced with the current
# value of Ceres_DIR being used by Foo. This should be passed as a hint
# when invoking find_package(Ceres) to ensure that the same install of
# Ceres is used as was used to build Foo.
set(CERES_DIR_HINTS @Ceres_DIR@)
# Forward the QUIET / REQUIRED options.
if (Foo_FIND_QUIETLY)
find_package(Ceres QUIET HINTS ${CERES_DIR_HINTS})
elseif (Foo_FIND_REQUIRED)
find_package(Ceres REQUIRED HINTS ${CERES_DIR_HINTS})
else ()
find_package(Ceres HINTS ${CERES_DIR_HINTS})
endif()
# Importing Ceres in FooConfig.cmake using CMake 3.x style.
#
# In CMake v3.x, the find_dependency() macro exists to forward the REQUIRED
# / QUIET parameters to find_package() when searching for dependencies.
#
# Note that find_dependency() does not take a path hint, so if Ceres was
# installed in a non-standard location, that location must be added to
# CMake's search list before this call.
include(CMakeFindDependencyMacro)
find_dependency(Ceres)
脚注
Ceres 及其依赖项有很多处于 homebrew/science 目录下。因此,如果你没有启动该选项,你需要先启动后再执行本次安装中的所有操作:
brew tap homebrew/science