Как использовать библиотеки в моем проекте CMake, которые необходимо установить в первую очередь?

у меня проблема с моей системой сборки CMake. Есть CMakeLists.txt файлы, определяющие время выполнения или библиотеки или использующие ExternalProjects_Add() для загрузки и создания внешнего кода. Из-за зависимостей эти проекты должны находить друг друга. Теперь я хочу иметь CMakeLists.txt на верхнем уровне, который строит все эти сразу. Для того, чтобы найти проект, должен быть установлен is. Но поиск проектов уже выполняется во время настройки в CMake.

repository
├─project
│ ├─game (Depends on engine, uses EngineConfig.cmake once installed)
│ │ ├─CMakeLists.txt
│ │ ├─include
│ │ ├─src
│ │ └─textures
│ ├─engine (Depends on boost, uses built-in FindBoost.cmake)
│ │ ├─CMakeLists.txt
│ │ ├─include
│ │ └─src
│ ├─boost (Not the source code, just an ExternalProject_Add call)
│ : └─CMakeLists.txt
│
├─build
│ ├─game
│ ├─engine
│ ├─boost (Source will be downloaded and built here)
│ : ├─download
│   ├─source
│   :
│
├─install
│ ├─game
│ │ ├─bin
│ │ └─textures
│ ├─engine
│ │ ├─include
│ │ │ └─engine
│ │ │   ├─EngineConfig.cmake (Needed to find the library)
│ │ │   :
│ │ │
│ │ └─lib
│ ├─boost (Layout is up to the external library)
│ : └─ ...
│
└─CMakeLists.txt (Calls add_subdirectory for all inside the project folder)

запустите процесс CMake для каждого проект: используя execute_process(${CMAKE_COMMAND} ...), Я могу настроить и построить каждый проект на настроить время. Однако это означает, что я всегда должен запускать CMake после редактирования кода и не могу скомпилировать из среды IDE, для которой я создал файлы проекта.

связывание с целями CMake: запуск процесса CMake для всех внешних библиотек в порядке, так как я не работаю над ними. Мои собственные библиотеки можно использовать, вызвав target_link_libraries() С их имена. Однако, связывая не достаточно. Мои библиотеки включают каталоги внешних библиотек. Они также должны быть доступны для проекта using.

как я могу использовать библиотеки в моем проекте CMake, которые необходимо установить в первую очередь?

3 ответов


вы можете классифицировать свои проекты на три группы:

  1. внешние зависимости, вы не работаете в этом супер-проекте
  2. проекты, над которыми вы работаете, но слишком сложны, чтобы добавить их в подкаталог, например, имеющие слишком много целей или по другим причинам. (У вас, похоже, нет такого проекта в вашем примере.)
  3. проекты, над которыми вы работаете: они будут добавлены в качестве подкаталога супер-проект.

перед настройкой супер-проекта необходимо настроить, построить и установить проект в группах #1 и #2:

  • вы можете сделать это перед запуском супер-проекта CMakeLists.txt, например, из shell-скрипта
  • или, как вы упомянули, из супер-проекта CMakeLists.txt, используя execute_process(${CMAKE_COMMAND} ...). Вы можете сделать это условно, используя результат соответствующий .

вам нужно решите, если проекты в группе #3, как engine будет использоваться только в проектах, которые используют их в качестве подкаталогов или вы собираетесь использовать их как автономные библиотеки, встроенные в свои собственные деревья сборки.

кроме того, вы упомянули, что: "мои библиотеки включают каталоги внешних библиотек". Давайте рассмотрим все такие возможные библиотеки engine может зависеть от:

  • сказать, LIB1 и LIB2 частные и общественные внешние зависимости engine и их конфигурационные модули экспортируют старую школу LIB1_* и LIB2_* переменные
  • LIB3 и LIB4 являются частными и публичными внешними зависимостями engine и их конфигурационные модули экспортируют LIB3 и LIB4 импортные библиотеки

под публичными и частными зависимостями я имею в виду, используется ли конкретная библиотека или не используется на интерфейсе engine.

теперь, если engine используется только в качестве подкаталог, то соответствующий раздел engine/CMakeLists.txt - это:

add_library(engine ...)
target_include_directories(engine
    PRIVATE
        ${LIB1_INCLUDE_DIRS}
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        ${LIB2_INCLUDE_DIRS})
target_compiled_definitions(engine
    PRIVATE ${LIB1_DEFINITIONS}
    PUBLIC ${LIB2_DEFINITIONS})
target_link_libraries(engine
    PRIVATE LIB3
    PUBLIC LIB4)

на repository/CMakeLists.txt:

add_subdirectory(engine)
add_subdirectory(game)

на game/CMakeLists.txt:

add_executable(game ...)
target_link_libraries(game engine)

include dirs как двигателя, так и его общедоступных зависимостей будут правильно перенаправлены в game.

если engine также будет построен в собственном дереве сборки (в другом проекте), вам нужно добавить код экспорта в engine/CMakeLists.txt и, возможно, пользовательский config-модуль, который вызывает find_package (или find_dependency) для свои зависимости. См.как использовать CMake для поиска и ссылки на библиотеку с помощью install-export и find_package? для сведения. Одна из проблем, не обсуждаемых в этом ответе, - это поиск зависимостей библиотеки в модуле конфигурации библиотеки:

ссылочный ответ SO просто устанавливает <lib>-targets.cmake скрипт, сгенерированный install(EXPORT ...) команда, как config-module:

install(EXPORT engine-targets
    FILE engine-config.cmake
    DESTINATION lib/cmake/engine)

это решение хорошо, когда engine не имеет дополнительных зависимостей. Если это так, они должны быть найдены в начале модуля config, который должен быть написан вручную.

engine/engine-config.cmake:

include(CMakeFindDependencyMacro)
find_dependency(some-dep-of-engine)
include(${CMAKE_CURRENT_LIST_DIR}/engine-targets.cmake)

и engine/CMakeLists.txt:

install(EXPORT engine-targets
    FILE engine-targets.cmake
    DESTINATION lib/cmake/engine)
install(FILES engine-config.cmake
    DESTINATION lib/cmake/engine)

Примечание:CMakeFindDependencyMacro был представлен в CMake 3.0. С более старым CMake вы можете использовать find_package вместо find_dependency (обработка тихих и необходимых параметров не будет перенаправлена в зависимость).


при экспорте библиотеки из engine project вам нужно указать его каталоги включения. Код ниже-это упрощение примера, приведенного в http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages. Пути adjasted для установки префикса install/engine для сборки и установки


спасибо @Tsyvarev и @tamas.кенез!--9--> вы за два хороших ответа. Я закончил тем, что использовал супер-строить шаблон. Проект верхнего уровня не делает много во время настройки. Во время сборки он запускает внешние процессы CMake для настройки, сборки и установки проектов.

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

# add_project(<project> [DEPENDS project...])
function(add_project PROJECT)
    cmake_parse_arguments(PARAM "" "" "DEPENDS" ${ARGN})
    add_custom_target(${PROJECT} ALL DEPENDS ${PARAM_DEPENDS})
    # Paths for this project
    set(SOURCE_DIR  ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT})
    set(BUILD_DIR   ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT})
    set(INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT})
    # Configure
    escape_list(CMAKE_MODULE_PATH)
    escape_list(CMAKE_PREFIX_PATH)
    add_custom_command(TARGET ${TARGET}
        COMMAND ${CMAKE_COMMAND}
            --no-warn-unused-cli
            "-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH_ESCAPED}"
            "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_ESCAPED}"
            -DCMAKE_BINARY_DIR=${BUILD_DIR}
            -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
            -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
            ${SOURCE_DIR}
        WORKING_DIRECTORY ${BUILD_DIR})
    # Build
    add_custom_command(TARGET ${TARGET}
        COMMAND ${CMAKE_COMMAND}
            --build .
            --target install
        WORKING_DIRECTORY ${BUILD_DIR})
    # Help later find_package() calls
    append_global(CMAKE_PREFIX_PATH ${INSTALL_DIR})
endfunction()

вот две вспомогательные функции. Мне потребовалось довольно много времени, чтобы выяснить правильный способ передачи параметров списка другим процессам CMake без их интерпретации и передачи Как нескольких параметров.

# escape_list(<list-name>)
function(escape_list LIST_NAME)
    string(REPLACE ";" "\;" ${LIST_NAME}_ESCAPED "${${LIST_NAME}}")
    set(${LIST_NAME}_ESCAPED "${${LIST_NAME}_ESCAPED}" PARENT_SCOPE)
endfunction()

# append_global(<name> value...)
function(append_global NAME)
    set(COMBINED "${${NAME}}" "${ARGN}")
    list(REMOVE_DUPLICATES COMBINED)
    set(${NAME} "${COMBINED}" CACHE INTERNAL "" FORCE)
endfunction()

единственным недостатком является то, что каждый проект должен иметь цель установки для этого. Поэтому вам нужно добавить фиктивную команду установки, такую как install(CODE "") для проектов, у которых нет команды install в противном случае, например, те, кто просто звонит ExternalProject_Add.