如何使用Git子模块和CMake处理传递依赖冲突?

问题描述 投票:12回答:1

我们有许多Git存储库,其中一些包含我们自己的代码,一些包含稍微修改过的第三方库代码。简化的依赖关系图如下所示:

  executable_A
    |     |
    |     v
    |  library_B
    |     |
    v     v
   library_C

因此,可执行文件在library_C上有两个依赖项,一个是直接的,一个是传递的。我希望使用Git子模块和CMake将它们组合在一起,因此简化的目录结构如下所示:

executable_A/
  CMakeListst.txt
  library_B/
    CMakeLists.txt
    library_C/
      CMakeLists.txt
  library_C/
    CMakeLists.txt

如您所见,library_C存储库作为子模块包含两次。让我们假设两个子模块都指向同一个提交(关于如何强制执行的任何想法都是受欢迎的,但不是这个问题的主题)。

我们正在使用add_subdirectorytarget_link_librariestarget_include_directories来管理这些相互依赖关系。很标准。

问题是如果你创建一个具有相同名称的目标两次,CMake不喜欢它,所以它会抱怨:

library_C / CMakeLists.txt上的CMake错误:13(add_library): add_library无法创建目标“library_C”,因为已存在具有相同名称的另一个目标。现有目标是在源目录“... / library_B / library_C”中创建的静态库。 有关更多详细信息,请参阅策略CMP0002的文档。

我宁愿不删除executable_Alibrary_C的直接依赖,因为它是通过library_B引入的事实是library_B的实现细节,不应该依赖。而且,一旦我们添加了像executable_A --> library_D --> library_C这样的另一个依赖项,这种方法就会崩溃。

This question是我能找到的最接近的,但是更为一般,但无论如何都没有答案。)

c++ cmake git-submodules
1个回答
10
投票

检测和丢弃项目的包含有几种方法,已经包含在主项目的其他部分中。

检查项目的目标存在

单个包含子项目的最简单模式是检查某个子项目的目标是否存在:

# When include 'C' subproject
if(NOT TARGET library_C)
    add_subdirectory(C)
endif()

(这里我们假设项目C定义了目标library_C。)

在有条件的包含之后,所有子项目的目标和功能将立即为呼叫者提供保证。

最好在所有地方使用这种模式(在executable_Alibrary_B中)。在library_B中改变library_Cexecutable_A顺序的方式并没有打破正确性。

这个模式可以重新设计,供子项目本身使用:

# At the beginning of 'C' project
cmake_minimum_required(...)
if(TARGET library_C)
    return() # The project has already been built.
endif()

project(C)
...

检查项目是否存在

创建项目时,CMake为其定义了几个变量,其中包含<PROJECT-NAME>_BINARY_DIR。请注意,此变量是高速缓存的,因此当第二次调用cmake时(例如,如果某些CMakeLists.txt已被更改),该变量一开始就存在。

# When include 'C' subproject
if(NOT C_BINARY_DIR # Check that the subproject has never been included
    OR C_BINARY_DIR STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/C" # Or has been included by us.
)
    add_subdirectory(C)
endif()

这个模式可以重新设计,供子项目本身使用:

# At the beginning of 'C' project
cmake_minimum_required(...)
if(NOT C_BINARY_DIR # Check that the project has never been created
    OR C_BINARY_DIR STREQUAL "${CMAKE_CURRENT_BINARY_DIR}" # Or has been created by us.
    project(C)
else()
    return() # The project has already been built
endif()
© www.soinside.com 2019 - 2024. All rights reserved.