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

22

我正在开发一个使用第三方库的项目。我可以将这些库克隆并作为git submodule放入我的项目,并使用add_subdirectory将其用于我的项目。但编译这些库需要很长时间,我无法在自己的项目中管理cmake变量,install()命令会使我的软件包包含许多我不需要的东西。

那么,在我的项目中正确使用第三方库的方法是什么呢?我无法要求客户安装这些库作为我的项目依赖项。

1个回答

55
回答这个问题需要涵盖几个方面,下面将介绍两个部分:
- config-file package - ExternalProject CMake module config-file package 如果您想要集成在项目范围之外构建的库,第一步是确保所有库都提供了config-file包。
config-file包通常包括像FooConfig.cmake、FooConfigVersion.cmake和FooTargets.cmake这样的文件。
一般来说,如果库Foo已经使用CMake并且已经提供了config-file包,则使用-DFoo_DIR:PATH=/path/to/build-or-install-dir/配置您的项目,允许您从自己的项目中调用find_package(Foo REQUIRED)。这将导入CMake目标,您可以将其链接到自己的库或可执行文件中。
现在,如果库Foo没有使用CMake,则有以下选项:
  • 情况1:

    • (a) 库 Foo 已经使用了 CMake
    • (b) 但是没有提供配置文件包
    • 行动:我建议改进他们的构建系统
  • 情况2:

    • (1) 库 Foo 没有使用 CMake
    • (2) Foo 的维护者愿意转换到 CMake(或至少与他们当前的构建系统并存)
    • 行动:我建议改进他们的构建系统
  • 情况3:

    • (1) 库 Foo 没有使用 CMake
    • (2) Foo 的维护者不想转换到 CMake
    • (3) 但是维护者愿意从他们当前的构建系统生成配置文件包
    • 行动:我建议帮助他们。例如 Qt5 就采取了这种做法,现在提供配置文件包。
  • 情况4:

    • (1) 库 Foo 没有使用 CMake

    • (2) Foo 的维护者不想(或尚未准备)转换到 CMake。

    • (3) 当前的构建系统不太好用,或者库在更广泛的编译器范围内很难构建,或者不支持交叉编译

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

      • 使用指向现有源代码树的路径配置项目
      • 让项目为您下载源代码
      • 例如 CPython 就采取了这种做法。GitHub 上有一个名为 python-cmake-buildsystem 的项目
  • 情况5:

    • (1) 由于任何原因,Foo 的维护者不想转换,或者无法维护替代构建系统,或者库已经可用于系统中
    • 行动:您可以创建一个 FindFoo.cmake,以创建导入目标。
      • 这样的文件可以针对您的项目进行特定配置,也可以直接贡献给 CMake
      • 例如,FindOpenSSL.cmakeFindGit.cmake 等都是如此

要了解有关config-file包的更多信息,请参见https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html

ExternalProject CMake模块

如果库Foo

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

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

有几种方法可以实现这一点。

以下是一个工作良好的方法:您可以设置一个我们称之为SuperBuild的2级构建系统。

为了支持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_link_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是两个独立的构建树,它们之间的链接是上述讨论的配置文件包。
通过使用-Foo_DIR: PATH = $ {CMAKE_BINARY_DIR} / Foo-build来配置AwesomeProject子项目,并调用find_package(Foo REQUIRED),实现了这一点。
如果您使用的是像VisualStudio这样的工具,则可以打开任何这些子目录中找到的解决方案文件。
了解有关外部项目的更多信息:https://cmake.org/cmake/help/latest/module/ExternalProject.html 结论
还有许多细节,但我希望这能让您更好地了解可能性。

7
“我建议改进他们的构建系统”完全没有帮助,根本没有提供任何有关如何解决问题的信息...这怎么成为顶级搜索结果了? - Tustin2121

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接