0

我试图了解在使用现代 CMake (3.13+) 来构建和包含供应商或子模块代码时的一些最佳实践。

假设我正在构建一个库 MyLib。我的文件结构是这样的

MyLib
|-CMakeLists.txt
|-src
|-include
|-submodules
 |-libgeos

在此示例中,我将 libgeos 作为 git 子模块包含在内,因为存在该依赖项,因此能够克隆项目并立即构建和运行测试非常方便。这也可以通过使用FetchContent或其他东西来解决,我的问题仍然存在;重要的是我不想依赖在构建环境中安装的 libgeos。

注意我随意选择了libgeos;我不知道是否将 libgeos 设置为适合此示例的 cmake 项目,但这都是理论上的,我只需要一些具体的库名称。请不要使用 libgeos 配置的具体细节来回答这个问题,除非 libgeos 是传统 cmake 的一个很好的例子。

但是现在,有一些其他项目想要使用我的项目,它需要 libgeos 并且不想依赖我的项目提供它。

OtherProject
|-CMakeLists.txt
|-src
|-include
|-submodules
 |-libgeos
 |-MyLib
  |submodules
  |-libgeos

当您克隆 OtherProject 时,您会得到两个版本的 libgeos,这可能不是很好;但这也不是一个大问题。也许它们不是同一个版本;假设 MyLib 需要 libgeos >= 2.0,因此 MyLib 包含 2.0,OtherProject 需要 libgeos>=2.1,因此 OtherProject 包含 libgeos >= 2.1。

现在我们可能会遇到一些构建问题。如果我们有以下行OtherProject/CMakeLists.txt

add_subdirectory(submodules/libgeos)

然后再一次,在同一行中MyLib/CMakeLists.txt,我们最终会出现 cmake 错误,因为libgeos在构建中定义了两次目标。这可以通过几种方式解决。

在添加之前检查 geos 是否存在

if(NOT TARGET geos)
  add_subdirectory(submodules/libgeos)
endif()

但是这个案例有一些问题;如果那个 blob 在OtherProject顶部,那很好,两个项目都使用 libgeos 2.1。但是如果它在OtherProject after add_subdirectory(submodules/MyLib)中,那么 geos 2.0 版本会被添加到构建中,这可能会或可能不会大声失败(希望它会失败)。

这也可以用find_package. 这两个项目都包括cmake/FindGeos.cmake使用上面的简介(if(NOT TARGET...))来添加geos构建,然后顶级项目cmake文件可以做到这一点

list(APPEND CMAKE_MODULE_PATH cmake)
find_package(geos 2) # (or 2.1)

那么他们尝试包含地理信息的顺序并不重要,因为它们都会推迟FindGeos.cmakeOtherProject因为它是模块路径中的第一个。

但是现在出现了一个新问题,有些ThirdProject 也想用MyLib,但是ThirdProject 想依赖libgeos系统环境中的哪个。它用于find_package(geos 2.1 CONFIG)使用已安装的GeosConfig.cmake文件,该文件添加geos::geos到 build 和 sets 中geos_FOUND。突然,MyLib 无法构建,因为geos_FOUND已设置,但我正在做target_link_library(mylib PUBLIC geos).

因此,这可以通过添加add_library(geos::geos ALIAS geos)两个自定义FindGeos.cmake文件来解决,那么无论 geos 是从源代码构建还是使用已安装的版本,目标名称都是相同的。

现在我们来回答我的实际问题:让我们从

  1. 我是不是疯了,没有人这样做,而我的团队试图使用 cmake 都错了?
  2. 是否有一些我刚刚完全错过的 cmake 功能可以解决所有这些问题?
  3. 我怀疑有很多书或演示文稿涵盖了这个主题,但我只是不知道该去哪里找,因为太多了;我应该看什么?我看过CMake Packages页面,当您使用根据该页面配置的所有项目时,它看起来可以解决问题;但它并没有真正回答如何弥合新旧项目之间的差距。

如果我不是疯了,也没有我可以看的直截了当的答案或演示文稿,那么

  1. MyLib 和 libgeos 的 cmake 配置应该是什么样子才能使这些情况起作用?
  • MyLib 是单独构建的
  • MyLib 是作为一个更大项目的一部分构建的,该项目提供不同版本的 geos
  • MyLib 是作为更大项目的一部分构建的,该项目依赖于环境中不同版本的地理信息

MyLibConfig.cmake我知道如果我想在环境中安装它,cmake 提供了可用于生成的助手。我还看到该export()函数存在,可用于将这些文件保存在构建树中的某个位置,然后find_package在配置模式下找到它们。但这对我来说有点奇怪,因为它不是多阶段构建,它只是cmakethen的一次调用make

但是可以说这是正确的答案,而 CMake forlibgeos并没有遵循它。FindGeos.cmake做这样的事情是否合适?

if(NOT geos_FOUND)
    add_subdirectory(submodules/libgeos)
    export(geos NAMESPACE geos)
    find_package(geos CONFIG)
endif()
4

0 回答 0