Перекрывающиеся зависимости между библиотеками в CMake

предположим, что существует следующая структура каталогов:

projects
   |
   +--lib1
   |   |
   |   +-CMakeFiles.txt
   |
   +--lib2
   |   |
   |   +-CMakeFiles.txt
   |
   +--test
       |
       +-CMakeFiles.txt

lib1/CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)

add_library(lib1 STATIC lib1.cpp)

lib2/CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)

add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_library(lib2 STATIC lib2.cpp)
target_link_libraries(lib2 lib1)

test / CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)
project(test)

add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)

add_executable(test main.cpp)
target_link_libraries(test lib1 lib2)

т. е. lib2 зависит от lib1 и test зависит от них обоих. (Я знаю, что технически статические библиотеки не" связываются", но это всего лишь пример).

проблема в том, что с текущей настройкой lib1 компилируется дважды-первый раз в " тесте" каталог сборки, а второй раз это в "тест/папка_сборки/lib2/папка_сборки". Я бы хотел избежать этого.

Я хочу иметь возможность добавлять зависимость от lib1, lib2 или обоих из них (используя add_subdirectory) к любому проекту, который находится в другом месте. Так трогательно CMakeFiles не вариант. Я также хочу избежать компиляции любой библиотеки несколько раз.

как я могу это сделать?

cmake-2.8.4 winxp sp3

--EDIT-- Верхнего уровня cmakelists не вариант, потому что я хочу сохранить чистый каталог верхнего уровня и иметь возможность включать библиотеки в другие проекты, которые могут быть расположены в другом месте. Поскольку это windows, я не могу "установить пакет в масштабах всей системы"-я не хочу терять способность переключать компилятор на лету. Служебные библиотеки, построенные с разными компиляторами, будут использовать разные библиотеки времени выполнения C / ABI и в результате будут несовместимы.

3 ответов


С CMake зависимости библиотеки являются транзитивными, поэтому вы не должны вызывать add_subdirectory два раза test/CMakeFiles.txt (и вам не нужно перечислять lib1 в зависимости test так как это уже зависимость lib2 ' s).

таким образом, вы можете изменить testС CMakeFiles.txt to:

cmake_minimum_required(VERSION 2.8.7)  # Prefer the most current version possible
project(test)

add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)

add_executable(test main.cpp)
target_link_libraries(test lib2)

кроме того, вы, вероятно, следует удалить cmake_minimum_required вызовы из ваших непроектных CMakeFiles.txt-файлы (lib-файлы). Для получения дополнительной информации запустите:

cmake --help-policy CMP0000


Этот установка по-прежнему приведет к перекомпиляции всех библиотек, если вы добавите аналогичный подкаталог test2 и проект, который зависит от lib1 и lib2. Если вы действительно не хотите иметь CMakeFiles верхнего уровня.txt в projects/, тогда вы застряли с тем, что вы делаете, или вы можете использовать либо export или .

export создать файл, который может быть included другими проектами и который импортирует цели в проект, который вызывает include.

install можно установить библиотеки в другой общий подкаталог projects/. В зависимости от структуры исходного каталога это может иметь преимущество только сделать заголовки API библиотеки доступными для зависимых проектов.

однако оба эти параметра требуют, чтобы зависимые проекты библиотеки были перестроены (и установлены), если они изменены, тогда как текущая настройка включает все зависимые цели в вашем проекте, поэтому любые изменение исходного файла в зависимой библиотеке вызовет ваш test цель устарела.

подробнее о export и install, run:

cmake --help-command export
cmake --help-command install

еще одно решение-добавить защиту в верхней части подкаталога-CMakeLists.txt:

if(TARGET targetname)
    return()
endif(TARGET targetname)

что заставит cmake ничего не делать при втором добавлении подкаталога (если targetname определяется в этом файле, конечно).

это приведет к созданию lib beeing в произвольном месте (в зависимости от того, какой модуль добавил его первым) в дереве сборки/, но он будет построен только один раз и связан везде.

в вашем примере, вы бы добавили

if(TARGET lib1)
    return()
endif(TARGET lib1)

в верхней части lib1/CMakeFiles.txt


возможно, добавьте CMakeLists верхнего уровня.txt в ваших проектах реж. Что-то вроде:

project( YourProjects )

add_subdirectory( lib1 )
add_subdirectory( lib2 )
add_subdirectory( test )

этого должно быть достаточно и даст вам решение-файл или makefile в вашем верхнем уровне сборки-dir. Затем вы должны удалить add_subdirectory( ../lib1 ... из ваших проектов lib1 и lib2, но вместо этого просто ссылаются на них. CMake будет знать, как найти lib1 и lib2 при компиляции теста.

т. е. в lib2:

project( lib2) 
add_library(lib2 STATIC lib2.cpp)
target_link_libraries(lib2 lib1)

и в тест:

project( test )
add_executable(test main.cpp)
target_link_libraries(test lib1 lib2)

дополнительный бонус: вы получит makefiles / solutionfiles для построения lib2 (с зависимым lib1) в каталоге lib2...