生活中充满红蓝药丸的选择,红色是真相,痛苦却现实,蓝色是假象,快乐而愚昧。就像电影《黑客帝国》,亲爱的读者,你肯定也会选择红药丸,勇敢探寻 CMake 依赖关系的真相。想了解其中的水有多深,请继续阅读!
任何复杂的软件都有其依赖关系,无论是系统API还是其他库调用,都是静态或动态进行连接。作为构建系统生成器,CMake 可以帮助我们以最自然的方式管理这些依赖关系。
案例1:开发人员处理标准软件的 CMake 依赖关系
开发人员会知道编译项目需要哪些依赖关系。在 CMakeLists.txt 文件中,开发人员根据需要标记这些标准包。例如,如果 OpenCV 是一个包,没有它项目将无法编译,因此它将被标记为:
find_package(OpenCV REQUIRED)
备注:建议阅读博客《如何使用 OpenCV 和 CMake》以更好地理解:<link to CMake_OpenCV_And_UnitTests>
一旦找到所需的包,你也会同时看到相关的头文件目录和其他设置。例如,你的项目需要 Boost 库,那么其 CMakeLists.txt 文件构造如下:
cmake_minimum_required (VERSION 3.8)
project(cmake_boost_demo)
find_package(Boost REQUIRED COMPONENTS date_time)
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIRS})
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
set(BOOST_ALL_DYN_LINK ON)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
如果找不到所需的包,则构建失败。你需要帮助 CMake 确定依赖关系,具体有两种方式:
- 使用 CMAKE_PREFIX_PATH
- 使用包含 <package>config.cmake 的特定 DIR 路径
下面的示例中使用了 Boost 库,我将用这个简单的例子分别演示这两种方法。这是主 .cpp 文件:
#include <iostream>
#include <boost/date_time/posix_time/posix_time_types.hpp>
using namespace std;
namespace pt = boost::posix_time;
int main(int argc, char** argv) {
pt::ptime now = pt::second_clock::local_time();
cout << “Year : ” << (int)now.date().year() << ” ”
<< “Month : ” << (int)now.date().month() << ” ”
<< “Day :” << (int)now.date().day() << endl;
return 0;
}
使用上述 CMakeLists.txt 文件,如果我们发布一个构建,我们会得到:
cmake -S. -BBuild .
Could NOT find Boost (missing: Boost_INCLUDE_DIR)
使用 CMAKE_PREFIX_PATH 方法,我们发布:
cmake -S. -BBuild . -DCMAKE_PREFIX_PATH=D:\boost_1_75_0
现在我们可以看到,生成成功(假设已经在 D:\boost_1_75_0 目录下安装了 Boost)
— Found Boost: D:/boost_1_75_0 (found version “1.75.0”) found components: date_time
使用 Boost_DIR 方法,我们可以发布如下构建:
cmake -S. -BBuild . -DBoost_DIR=D:\boost_1_75_0\lib64-msvc-14.2\cmake\Boost-1.75.0
在 D:\boost_1_75_0\lib64-msvc-14.1\cmake\Boost-1.75.0 文件夹中,你可以看到:
<package>Config.cmake 文件就在目录下方,这也是确定 CMake 依赖关系的文件。
然后,你可以得到想要的输出:
案例2:开发人员处理内部库 CMake 依赖关系

如果项目内部有顶级项目依赖的库,则可以将 CMakeLists.txt 文件细分成很多小文件,将顶级 CMakeLists.txt 文件指定依赖项分散。可以通过以下方式实现:
· add_library + target_link_libraries· add_subdirectory
在现代 CMake中,很少会使用 add_dependencies 选项,因此我没有把 CMake add_dependencies 列出来。我们先来看看如何使用 add_ 子目录来添加依赖项。
最好的例子就是 CMake 自身的构建。你可以看看 CMakeLists.txt 文件的位置:https://github.com/Kitware/CMake/blob/master/CMakeLists.txt
你可以看到 20 个 add_ 子目录实例,其中第一个给出了清晰的图片:
# Build CMake std library for CMake and CTest.
set(CMAKE_STD_LIBRARY cmstd)
add_subdirectory(Utilities/std)
Utilities/Std 文件夹内可以找到 CMakeLists.txt 文件,如下:
# To ensure maximum portability across various compilers and platforms
# deactivate any compiler extensions
set(CMAKE_CXX_EXTENSIONS FALSE)
# source files for CMake std library
set(SRCS cm/bits/fs_path.cxx
cm/bits/string_view.cxx
cm/filesystem
cm/memory
cm/optional
cm/shared_mutex
cm/string_view
cm/utility
cmext/string_view)
add_library(cmstd STATIC ${SRCS})
这清楚地展示了 CMAKE_STD_LIBRARY cmstd 是如何构建的。具体可查看博客文章《CMake、OpenCV 和单元测试》,找到使用 target_link_libraries 的例子。
注意:想了解 CMake add_dependencies 的用法示例,请参考https://github.com/spurious/SDL-mirror/blob/master/test/CMakeLists.txt。很明显,这里没有使用现代 CMake。
结语:
如果你是开发人员,想要了解 CMake C++ 依赖关系有复杂,水有多深?这篇博客就是为你而写的,我们深入探讨了用户处理 CMake 依赖关系的方法,希望能对你有所帮助。