У нас есть несколько репозиториев 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
,
(Этот вопрос это самое близкое, что я мог найти, но оно более общее и остается без ответа.)
Существует несколько подходов для обнаружения и отмены включения проекта, которые уже включены в некоторые другие части основного проекта.
Самым простым шаблоном для одиночного включения подпроекта является проверка существования цели какого-либо подпроекта:
# 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()
Других решений пока нет …