cmake项目中使用第三方库的正确方法

Correct way to use third-party libraries in cmake project

我正在处理使用一些第三方库的项目。我可以将它们作为 git submodule 克隆并放入我的项目中,并使用 add_subdirectory 在我的项目中使用它们。但是编译这些库需要很多时间,而且我无法在我的项目中管理 cmake 变量,并且 install() 命令使我的包包含许多我不需要的东西。

那么在我的项目中使用第三方库的正确方法是什么。我不能告诉客户端安装这些库作为我项目的依赖项。

回答这个问题需要涵盖几个方面,您会发现以下两个部分:

  • 配置文件包
  • ExternalProject CMake 模块

配置文件包

如果您正在考虑集成不在项目范围内构建的库,第一步是确保这些库都提供配置文件包。

配置文件包通常包含 FooConfig.cmakeFooConfigVersion.cmakeFooTargets.cmake.

等文件

一般来说,如果库 Foo 已经在使用 CMake 并且已经提供了一个配置文件包,使用 -DFoo_DIR:PATH=/path/to/build-or-install-dir/ 配置您的项目允许您从自己的调用 find_package(Foo REQUIRED)项目。这将导入您可以 link 针对您自己的库或可执行文件的 CMake 目标。

现在,如果库 Foo 尚未使用 CMake,则有以下选项:

  • 案例一:

    • (a) 库 Foo 已经在使用 CMake
    • (b) 但不要 提供配置文件包
    • 行动:我建议改进他们的构建系统
  • 案例二:

    • (1) 库 Foo 使用 CMake
    • (2) 和 Foo 的维护者愿意过渡到 CMake(或者至少在他们当前的构建系统旁边有 CMakeLists.txt
    • 行动:我建议改进他们的构建系统
  • 案例三:

    • (1) 库 Foo 使用 CMake
    • (2) 和 Foo 的维护者不想过渡到 CMake
    • (3) 但维护者愿意从他们当前的构建系统生成配置文件包
    • 行动:我建议帮助他们。例如,这是为 Qt5 所做的,它现在提供了一个配置文件包。
  • 案例四:

    • (1) 库 Foo 不是 使用 CMake

    • (2) 和 Foo 的维护者不想(或尚未准备好)过渡到 CMake。

    • (3) 并且当前构建系统运行不佳,或者库难以使用更广泛的编译器构建,或者不支持交叉编译

    • action:创建一个名为 foo-cmake-buildsystem 的项目(最好在 GitHub 上),允许通过以下任一方式构建库

      • 使用现有源代码树的路径配置项目
      • 让项目为您下载源代码
      • 例如,这是为 CPython 完成的。 GitHub
      • 上有一个名为 python-cmake-buildsystem 的项目
  • 案例五:

    • (1) Foo 的维护者出于任何原因不想转换,或者维护替代构建系统是不可能的,或者库已经在系统上可用
    • action:您可以创建一个 FindFoo.cmake 来创建导入的目标。
      • 此类文件可以特定于您的项目,也可以直接贡献给 CMake
      • 例如 FindOpenSSL.cmakeFindGit.cmake、...
      • 的情况

要了解有关配置文件包的更多信息,请参阅 https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html

ExternalProject CMake 模块

如果图书馆 Foo 是:

  • (1) 在系统上不可用:
    • 或者无法使用包管理器安装
    • 或与维护软件包(debian、conda-forge、chocolatey 等)的社区合作以获得这样的软件包是不可能的
  • (2) 或者需要为你的项目专门编译

然后,ExternalProject CMake 模块将允许您从自己的项目中下载、配置、构建...这些项目。

实现这一目标的方法很少。

这是一个运行良好的系统:您可以设置一个我们称之为的 2 级构建系统:SuperBuild.

为了支持 SuperBuild 方法,您的 CMakeLists.txt 可以具有以下结构:

project(AwesomeProject)

[...]

option(Awesome_ENABLE_EXTRA "Enable more awesome stuff" OFF)

option(AwesomeProject_SUPERBUILD "Build ${PROJECT_NAME} and the projects it depends on." ON)

[...]

if(AwesomeProject_SUPERBUILD)
  include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
  return()
endif()

find_package(Foo REQUIRED)

add_library(AwesomeLib ....)
target_library_libraries(AwesomeLib PUBLIC Foo)

[...]

然后,在文件 SuperBuild.cmake 中,您大致会有以下两个调用:

ExternalProject_Add(Foo
  GIT_REPOSITORY "git://github.com/Foo/Foo"
  GIT_TAG "123456"
  SOURCE_DIR ${CMAKE_BINARY_DIR}/Foo
  BINARY_DIR ${CMAKE_BINARY_DIR}/Foo-build
  CMAKE_CACHE_ARGS
    -DFOO_ENABLE_BAR:BOOL=1
  INSTALL_COMMAND ""
  )


ExternalProject_Add(AwesomeProject
  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
  BINARY_DIR ${CMAKE_BINARY_DIR}/AwesomeProject-build
  DOWNLOAD_COMMAND ""
  UPDATE_COMMAND ""
  CMAKE_CACHE_ARGS
    -Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
    -DAwesome_ENABLE_EXTRA:BOOL=${Awesome_ENABLE_EXTRA}
  INSTALL_COMMAND ""
  )

这意味着您通常的构建树现在可以在子目录 AwesomeProject-build.

中找到

注意Foo-buildAwesomeProject-build是两个独立的构建树,它们之间的link 就是上面讨论的配置文件包。

这可以通过使用 -Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build 配置 AwesomeProject 子项目来实现 和调用 find_package(Foo REQUIRED).

如果您使用 VisualStudio 等工具,您可以打开在这些子目录中找到的解决方案文件。

要了解有关外部项目的更多信息:https://cmake.org/cmake/help/latest/module/ExternalProject.html

结论

还有很多细节,但我希望这能让您更好地了解什么是可能的。