如何搞定 CMake 依赖关系?(冗余还是依赖?-取决于你的选择)

Blog
Author:
Dori ExtermanDori Exterman
Published On:
4月 10, 2021
Estimated reading time:
1 minutes

生活中充满红蓝药丸的选择,红色是真相,痛苦却现实,蓝色是假象,快乐而愚昧。就像电影《黑客帝国》,亲爱的读者,你肯定也会选择红药丸,勇敢探寻 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 依赖关系的文件。

然后,你可以得到想要的输出:

CMake-boost-output

案例2:开发人员处理内部库 CMake 依赖关系
dependencies_developer

如果项目内部有顶级项目依赖的库,则可以将 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 依赖关系的方法,希望能对你有所帮助。