如何使用 Git 子模块和 CMake 处理传递依赖冲突?
How to handle a transitive dependency conflict using Git submodules and CMake?
我们有许多 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_subdirectory
、target_link_libraries
和 target_include_directories
来管理这些相互依赖关系。相当标准。
问题是如果你创建一个同名的目标两次,CMake 不喜欢它,所以它会抱怨:
CMake Error at library_C/CMakeLists.txt:13 (add_library):
add_library cannot create target "library_C" because another target
with the same name already exists. The existing target is a static
library created in source directory ".../library_B/library_C".
See documentation for policy CMP0002 for more details.
我不想删除 executable_A
对 library_C
的直接依赖,因为它是通过 library_B
引入的,这是 [=20= 的一个实现细节] 不应依赖。此外,一旦我们添加另一个依赖项,如 executable_A --> library_D --> library_C
.
,这种方法就会崩溃
(This question 是我能找到的最接近的,但更笼统一点,而且仍然没有答案。)
有几种检测和丢弃项目包含的方法,该项目已经包含在主项目的其他一些部分中。
检查项目的目标是否存在
单个包含子项目的最简单模式是检查某个子项目的目标是否存在:
# When include 'C' subproject
if(NOT TARGET library_C)
add_subdirectory(C)
endif()
(这里假设项目C
定义了目标library_C
。)
在这种有条件的包含之后,所有子项目的目标和函数将 立即可用 供调用者 with garantee。
最好在所有地方(在executable_A
和library_B
中)使用此模式。这种改变 executable_A
中 library_B
和 library_C
顺序的方式不会破坏正确性。
此模式可以重新设计以供子项目本身使用:
# 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就在其中。注意,这个变量是cached,所以当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()
CMake 3.10 及更高版本现在支持:
include_guard(GLOBAL)
类似于 c++ 中的#pragma once。
Craig Scott 写的 "Professional CMAKE, A Practical Guide" 一本关于 cmake 的好书对我解释了很多。仅在线提供:
[https://crascit.com/professional-cmake/][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_subdirectory
、target_link_libraries
和 target_include_directories
来管理这些相互依赖关系。相当标准。
问题是如果你创建一个同名的目标两次,CMake 不喜欢它,所以它会抱怨:
CMake Error at library_C/CMakeLists.txt:13 (add_library):
add_library cannot create target "library_C" because another target with the same name already exists. The existing target is a static library created in source directory ".../library_B/library_C".
See documentation for policy CMP0002 for more details.
我不想删除 executable_A
对 library_C
的直接依赖,因为它是通过 library_B
引入的,这是 [=20= 的一个实现细节] 不应依赖。此外,一旦我们添加另一个依赖项,如 executable_A --> library_D --> library_C
.
(This question 是我能找到的最接近的,但更笼统一点,而且仍然没有答案。)
有几种检测和丢弃项目包含的方法,该项目已经包含在主项目的其他一些部分中。
检查项目的目标是否存在
单个包含子项目的最简单模式是检查某个子项目的目标是否存在:
# When include 'C' subproject
if(NOT TARGET library_C)
add_subdirectory(C)
endif()
(这里假设项目C
定义了目标library_C
。)
在这种有条件的包含之后,所有子项目的目标和函数将 立即可用 供调用者 with garantee。
最好在所有地方(在executable_A
和library_B
中)使用此模式。这种改变 executable_A
中 library_B
和 library_C
顺序的方式不会破坏正确性。
此模式可以重新设计以供子项目本身使用:
# 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就在其中。注意,这个变量是cached,所以当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()
CMake 3.10 及更高版本现在支持:
include_guard(GLOBAL)
类似于 c++ 中的#pragma once。
Craig Scott 写的 "Professional CMAKE, A Practical Guide" 一本关于 cmake 的好书对我解释了很多。仅在线提供: [https://crascit.com/professional-cmake/][1]
我不是克雷格,但他的书对培养像我这样的新人很有帮助。