Как справиться с транзитивным конфликтом зависимостей, используя подмодули Git и 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 Ошибка в library_C / CMakeLists.txt: 13 (add_library):
add_library не может создать цель «library_C», потому что другая цель
с таким именем уже существует. Существующая цель является статической
библиотека создана в исходном каталоге «… / library_B / library_C».
См. Документацию для политики CMP0002 для более подробной информации.

Я бы не хотел удалять прямую зависимость executable_A на library_C, потому что тот факт, что он втягивается через library_B это деталь реализации library_B на это не следует полагаться. Более того, этот подход сломается, как только мы добавим еще одну зависимость, такую ​​как executable_A --> library_D --> library_C,

(Этот вопрос это самое близкое, что я мог найти, но оно более общее и остается без ответа.)

12

Решение

Существует несколько подходов для обнаружения и отмены включения проекта, которые уже включены в некоторые другие части основного проекта.

Проверьте наличие цели проекта

Самым простым шаблоном для одиночного включения подпроекта является проверка существования цели какого-либо подпроекта:

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

(Здесь мы предполагаем, что проект C определяет цель library_C.)

После такого условного включения все цели и функции подпроекта будут Сразу Доступно для звонящего с гарантией.

Лучше использовать этот шаблон во всех местах (в executable_A а также library_B). Такой способ изменения порядка library_B а также library_C в executable_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 определяет несколько переменных для него, и <ПРОЕКТ-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()
10

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]