Spec-Zone.ru › CMake 3.29

Руководство по импорту и экспорту

  • Введение
  • Импорт целевых компонентов

    • Импорт исполняемых файлов
    • Импорт библиотек
  • Экспорт целевых компонентов

    • Создание пакетов

      • Создание файла конфигурации пакета
      • Создание файла версии пакета
    • Экспорт целевых компонентов из дерева сборки
    • Сборка и установка пакета
  • Создание переносимых пакетов
  • Использование файла конфигурации пакета
  • Добавление компонентов

Введение

В этом руководстве мы рассмотрим концепцию целевых компонентов IMPORTED и продемонстрируем, как импортировать существующие исполняемые файлы или библиотеки с диска в проект CMake. Затем мы покажем, как CMake поддерживает экспорт целевых компонентов из одного проекта, основанного на CMake, и их импорт в другой. Наконец, мы продемонстрируем, как упаковать проект с файлом конфигурации для легкой интеграции в другие проекты CMake. Это руководство и полный пример исходного кода можно найти в каталоге Help/guide/importing-exporting дерева исходного кода CMake.

Импорт целевых компонентов

Целевые компоненты IMPORTED используются для преобразования файлов за пределами проекта CMake в логические целевые компоненты внутри проекта. Целевые компоненты IMPORTED создаются с помощью параметра IMPORTED команд add_executable() и add_library(). Для целевых компонентов IMPORTED файлы сборки не генерируются. После импорта целевые компоненты IMPORTED могут быть использованы как любые другие целевые компоненты в проекте и обеспечивают удобную и гибкую ссылку на внешние исполняемые файлы и библиотеки.

По умолчанию имя целевого компонента IMPORTED имеет область видимости в каталоге, в котором он создан, и ниже. Мы можем использовать параметр GLOBAL для расширения видимости, чтобы целевой компонент был доступен глобально в системе сборки.

Подробные сведения о целевом компоненте IMPORTED задаются путем установки свойств, имена которых начинаются с IMPORTED_ и INTERFACE_. Например, IMPORTED_LOCATION содержит полный путь к целевому компоненту на диске.

Импорт исполняемых файлов

Для начала мы рассмотрим простой пример создания целевого компонента исполняемого файла IMPORTED и его последующего использования в команде add_custom_command().

Для начала нам потребуется выполнить некоторые настройки. Мы хотим создать исполняемый файл, который при запуске создаёт базовый файл main.cc в текущем каталоге. Подробности этого проекта не важны. Перейдите в Help/guide/importing-exporting/MyExe, создайте каталог сборки, запустите cmake и выполните сборку и установку проекта.

$ cd Help/guide/importing-exporting/MyExe
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
$ cmake --install . --prefix <install location>
$ <install location>/myexe
$ ls
[...] main.cc [...]

Теперь мы можем импортировать этот исполняемый файл в другой проект CMake. Исходный код для этой части доступен в Help/guide/importing-exporting/Importing. В файле CMakeLists используйте команду add_executable() для создания нового целевого компонента под названием myexe. Используйте параметр IMPORTED для указания CMake, что этот целевой компонент ссылается на исполняемый файл, расположенный вне проекта. Правила сборки для него не будут генерироваться, и свойство целевого компонента IMPORTED будет установлено в true.

add_executable(myexe IMPORTED)

Далее, установите свойство IMPORTED_LOCATION целевого компонента с помощью команды set_property(). Это укажет CMake на расположение целевого компонента на диске. Путь может потребоваться скорректировать в соответствии с <install location> , указанным на предыдущем шаге.

set_property(TARGET myexe PROPERTY
             IMPORTED_LOCATION "../InstallMyExe/bin/myexe")

Теперь мы можем использовать этот целевой компонент IMPORTED точно так же, как любой другой целевой компонент, собранный внутри проекта. В данном случае, давайте представим, что мы хотим использовать сгенерированный исходный файл в нашем проекте. Используйте целевой компонент IMPORTED в команде add_custom_command():

add_custom_command(OUTPUT main.cc COMMAND myexe)

Поскольку COMMAND задаёт имя исполняемого файла, оно автоматически будет заменено путем к исполняемому файлу, указанным в свойстве IMPORTED_LOCATION выше.

Наконец, используйте выходные данные из add_custom_command():

add_executable(mynewexe main.cc)

Импорт библиотек

Аналогичным образом, к библиотекам из других проектов можно получить доступ через целевые компоненты IMPORTED.

Примечание: Полный исходный код примеров в этом разделе не предоставлен и оставлен в качестве упражнения для читателя.

В файле CMakeLists добавьте библиотеку IMPORTED и укажите ее расположение на диске:

add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY
             IMPORTED_LOCATION "/path/to/libfoo.a")

Затем используйте библиотеку IMPORTED в нашем проекте:

add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE foo)

В Windows библиотеку .dll и ее импортируемую библиотеку .lib можно импортировать вместе:

add_library(bar SHARED IMPORTED)
set_property(TARGET bar PROPERTY
             IMPORTED_LOCATION "c:/path/to/bar.dll")
set_property(TARGET bar PROPERTY
             IMPORTED_IMPLIB "c:/path/to/bar.lib")
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE bar)

Библиотеку с несколькими конфигурациями можно импортировать с помощью одного целевого компонента:

find_library(math_REL NAMES m)
find_library(math_DBG NAMES md)
add_library(math STATIC IMPORTED GLOBAL)
set_target_properties(math PROPERTIES
  IMPORTED_LOCATION "${math_REL}"
  IMPORTED_LOCATION_DEBUG "${math_DBG}"
  IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE math)

Сгенерированная система сборки слинкует myexe к m.lib при сборке в релизной конфигурации и к md.lib при сборке в отладочной конфигурации.

Экспорт целевых компонентов

Хотя целевые компоненты IMPORTED сами по себе полезны, они всё же требуют, чтобы проект, который их импортирует, знал расположение файлов целевого компонента на диске. Настоящая сила целевых компонентов IMPORTED проявляется, когда проект, предоставляющий файлы целевого компонента, также предоставляет файл CMake для их импорта. Проект может быть настроен для создания необходимой информации, чтобы его можно было легко использовать другими проектами CMake, будь то из каталога сборки, локальной установки или при упаковке.

В следующих разделах мы пошагово разберём примеры проектов. Первый проект создаст и установит библиотеку и соответствующие файлы конфигурации и пакета. Второй проект будет использовать сгенерированный пакет.

Давайте начнём с проекта MathFunctions в каталоге Help/guide/importing-exporting/MathFunctions. Здесь у нас есть заголовочный файл MathFunctions.h, который объявляет функцию sqrt:

#pragma once

namespace MathFunctions {
double sqrt(double x);
}

И соответствующий файл исходного кода MathFunctions.cxx:

#include "MathFunctions.h"

#include <cmath>

namespace MathFunctions {
double sqrt(double x)
{
  return std::sqrt(x);
}
}

Не стоит слишком сильно зацикливаться на особенностях C++ файлов; они просто предназначены для создания простого примера, который будет компилироваться и работать на многих системах сборки.

Теперь мы можем создать файл CMakeLists для проекта CMakeLists.txt проекта MathFunctions. Начните с указания версии cmake_minimum_required() и имени проекта project():

cmake_minimum_required(VERSION 3.15)
project(MathFunctions)

# make cache variables for install destinations
include(GNUInstallDirs)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Модуль GNUInstallDirs включён для того, чтобы предоставить проекту гибкость установки в различные структуру каталогов платформ, сделав каталоги доступными в качестве переменных кэша.

Создайте библиотеку под названием MathFunctions с помощью команды add_library():

add_library(MathFunctions STATIC MathFunctions.cxx)

Затем используйте команду target_include_directories(), чтобы указать каталоги включения для целевого объекта:

target_include_directories(MathFunctions
                           PUBLIC
                           "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
                           "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)

Нам нужно сообщить CMake, что мы хотим использовать разные каталоги включения в зависимости от того, строим ли мы библиотеку или используем её из установленного места. Если этого не сделать, при создании CMake экспорта будет экспортирован путь, специфичный для текущей директории сборки, и он не будет действительным для других проектов. Мы можем использовать generator expressions, чтобы указать, что при построении библиотеки следует использовать текущую директорию исходников. В противном случае, при установке, используйте каталог include. Подробности см. в разделе «Создание переносимых пакетов».

Команды install(TARGETS) и install(EXPORT) работают вместе, чтобы установить как целевые объекты (в нашем случае библиотеку), так и файл CMake, предназначенный для удобного импорта целевых объектов в другой проект CMake.

Сначала в команде install(TARGETS) мы укажем целевой объект, имя EXPORT и назначения, которые расскажут CMake, куда установить целевые объекты.

install(TARGETS MathFunctions
        EXPORT MathFunctionsTargets
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

Здесь опция EXPORT сообщает CMake о создании экспорта под названием MathFunctionsTargets. Сгенерированные целевые объекты IMPORTED имеют соответствующие свойства, определяющие их требования к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и другие соответствующие встроенные свойства INTERFACE_. Вариант INTERFACE пользовательских свойств, перечисленных в COMPATIBLE_INTERFACE_STRING и других Свойства совместимого интерфейса также передаются сгенерированным целевым объектам IMPORTED. Например, в данном случае целевой объект IMPORTED будет иметь свойство INTERFACE_INCLUDE_DIRECTORIES, заполненное каталогом, указанным свойством INCLUDES DESTINATION. Так как был задан относительный путь, он обрабатывается как относительный к CMAKE_INSTALL_PREFIX.

Обратите внимание, что мы не просили CMake установить экспорт пока.

Не забываем установить заголовочный файл MathFunctions.h с помощью команды install(FILES). Заголовочный файл должен быть установлен в каталог include, как указано в команде target_include_directories() выше.

install(FILES MathFunctions.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

Теперь, когда библиотека MathFunctions и заголовочный файл установлены, нам также нужно явно установить детали экспорта MathFunctionsTargets. Используйте команду install(EXPORT), чтобы экспортировать целевые объекты в MathFunctionsTargets, как определено командой install(TARGETS).

install(EXPORT MathFunctionsTargets
        FILE MathFunctionsTargets.cmake
        NAMESPACE MathFunctions::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

Эта команда генерирует файл MathFunctionsTargets.cmake и организует его установку в ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions. Файл содержит код, подходящий для использования последующими проектами для импорта всех целевых объектов, перечисленных в команде установки, из дерева установки.

Опция NAMESPACE добавит MathFunctions:: к именам целевых объектов, когда они записываются в файл экспорта. Эта конвенция с двойными двоеточиями даёт CMake подсказку, что имя является целевым объектом IMPORTED, когда он используется последующими проектами. Таким образом, CMake может вывести сообщение об ошибке, если пакет, его предоставляющий, не найден.

Сгенерированный файл экспорта содержит код, создающий библиотеку IMPORTED.

# Create imported target MathFunctions::MathFunctions
add_library(MathFunctions::MathFunctions STATIC IMPORTED)

set_target_properties(MathFunctions::MathFunctions PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)

Этот код очень похож на пример, который мы создали вручную в разделе «Импорт библиотек». Обратите внимание, что ${_IMPORT_PREFIX} вычисляется относительно расположения файла.

Внешний проект может загрузить этот файл с помощью команды include() и обратиться к библиотеке MathFunctions из дерева установки, как если бы она была построена в собственном дереве. Например:

1 include(GNUInstallDirs)
2 include(${INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions/MathFunctionTargets.cmake)
3 add_executable(myexe src1.c src2.c )
4 target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

Строка 2 загружает целевой файл CMake. Хотя мы экспортировали только один целевой объект, этот файл может импортировать любое количество целевых объектов. Их расположения вычисляются относительно расположения файла, чтобы дерево установки можно было легко переместить. Строка 4 ссылается на импортированную библиотеку MathFunctions. Результирующая система сборки будет ссылаться на библиотеку из её установленного расположения.

Исполняемые файлы также могут быть экспортированы и импортированы с помощью того же процесса.

Любое количество установок целевых объектов может быть связано с одним именем экспорта. Имена экспорта считаются глобальными, поэтому любой каталог может внести вклад в установку целевого объекта. Команда install(EXPORT) должна быть вызвана только один раз для установки файла, который ссылается на все целевые объекты. Ниже приведен пример того, как несколько экспортов могут быть объединены в один файл экспорта, даже если они находятся в разных подкаталогах проекта.

# A/CMakeLists.txt
add_executable(myexe src1.c)
install(TARGETS myexe DESTINATION lib/myproj
        EXPORT myproj-targets)

# B/CMakeLists.txt
add_library(foo STATIC foo1.c)
install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)

# Top CMakeLists.txt
add_subdirectory (A)
add_subdirectory (B)
install(EXPORT myproj-targets DESTINATION lib/myproj)

Создание пакетов

На данном этапе проект MathFunctions экспортирует информацию о целевых объектах, необходимую для использования другими проектами. Мы можем сделать этот проект ещё более удобным для использования другими проектами, сгенерировав файл конфигурации, чтобы команда CMake find_package() могла найти наш проект.

Для начала нам нужно внести несколько изменений в файл CMakeLists.txt. Сначала включите модуль CMakePackageConfigHelpers, чтобы получить доступ к некоторым вспомогательным функциям для создания файлов конфигурации.

include(CMakePackageConfigHelpers)

Затем мы создадим файл конфигурации пакета и файл версии пакета.

Создание файла конфигурации пакета

Используйте команду configure_package_config_file(), предоставляемую модулем CMakePackageConfigHelpers, для генерации файла конфигурации пакета. Обратите внимание, что эту команду следует использовать вместо обычной команды configure_file(). Это помогает гарантировать, что полученный пакет переносимый, избегая жёстко закодированных путей в установленном файле конфигурации. Путь, переданный в INSTALL_DESTINATION, должен быть местом назначения, куда будет установлен файл MathFunctionsConfig.cmake. Мы рассмотрим содержимое файла конфигурации пакета в следующем разделе.

configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

Установите сгенерированные файлы конфигурации с помощью команды INSTALL(files). Файлы MathFunctionsConfigVersion.cmake и MathFunctionsConfig.cmake устанавливаются в одно и то же место, завершая создание пакета.

install(FILES
          "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
          "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

Теперь нам нужно создать сам файл конфигурации пакета. В данном случае файл Config.cmake.in очень прост, но достаточно, чтобы позволить последующим проектам использовать целевые объекты IMPORTED.

@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake")

check_required_components(MathFunctions)

Первая строка файла содержит только строку @PACKAGE_INIT@. Она расширяется при конфигурации файла и позволяет использовать переносимые пути, начинающиеся с PACKAGE_. Она также предоставляет макросы set_and_check() и check_required_components().

Макрос check_required_components проверяет, что все необходимые, не необязательные компоненты найдены, проверяя переменные <Package>_<Component>_FOUND для всех требуемых компонентов. Этот макрос должен вызываться в конце файла конфигурации пакета, даже если пакет не имеет компонентов. Таким образом, CMake может убедиться, что проект, находящийся ниже по потоку, не указал какие-либо несуществующие компоненты. Если check_required_components завершится неудачей, переменная <Package>_FOUND устанавливается в FALSE, и пакет считается не найденным.

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

Если какие-либо макросы должны предоставляться пакетом MathFunctions, они должны храниться в отдельном файле, который устанавливается в такое же расположение, как и файл MathFunctionsConfig.cmake, и включаться оттуда.

Все необходимые зависимости пакета также должны быть найдены в файле конфигурации пакета. Представим, что нам необходима библиотека Stats в нашем проекте. В файле CMakeLists мы добавим:

find_package(Stats 2.6.4 REQUIRED)
target_link_libraries(MathFunctions PUBLIC Stats::Types)

Поскольку целевой объект Stats::Types является зависимостью MathFunctions, проекты, расположенные ниже по потоку, также должны найти пакет Stats и связаться с библиотекой Stats::Types. Пакет Stats должен быть найден в файле конфигурации для обеспечения этого.

include(CMakeFindDependencyMacro)
find_dependency(Stats 2.6.4)

Макрос find_dependency из модуля CMakeFindDependencyMacro помогает, распространяя, является ли пакет REQUIRED, или QUIET, и т. д. Макрос find_dependency также устанавливает MathFunctions_FOUND в False, если зависимость не найдена, вместе с диагностическим сообщением, что пакет MathFunctions не может быть использован без пакета Stats.

Упражнение: Добавьте необходимую библиотеку в проект MathFunctions.

Создание файла версии пакета

Модуль CMakePackageConfigHelpers предоставляет команду write_basic_package_version_file() для создания простого файла версии пакета. Этот файл читается CMake, когда вызывается find_package(), чтобы определить совместимость с запрошенной версией и установить некоторые переменные, специфичные для версии, такие как <PackageName>_VERSION, <PackageName>_VERSION_MAJOR, <PackageName>_VERSION_MINOR, и т. д. См. документацию cmake-packages для получения более подробной информации.

set(version 3.4.1)

set_property(TARGET MathFunctions PROPERTY VERSION ${version})
set_property(TARGET MathFunctions PROPERTY SOVERSION 3)
set_property(TARGET MathFunctions PROPERTY
  INTERFACE_MathFunctions_MAJOR_VERSION 3)
set_property(TARGET MathFunctions APPEND PROPERTY
  COMPATIBLE_INTERFACE_STRING MathFunctions_MAJOR_VERSION
)

# generate the version file for the config file
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${version}"
  COMPATIBILITY AnyNewerVersion
)

В нашем примере, MathFunctions_MAJOR_VERSION определён как COMPATIBLE_INTERFACE_STRING, что означает, что он должен быть совместим среди зависимостей любого потребителя. Установив эту пользовательскую свойство в этой и следующей версии MathFunctions, cmake выдаст диагностическое сообщение, если есть попытка использовать версию 3 вместе с версией 4. Пакеты могут использовать такую схему, если разные основные версии пакета предназначены для несовместимости.

Экспорт целевых объектов из дерева сборки

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

Если мы хотим, чтобы наш пример проекта также использовался из каталога сборки, нам нужно добавить следующее в CMakeLists.txt:

export(EXPORT MathFunctionsTargets
       FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/MathFunctionsTargets.cmake"
       NAMESPACE MathFunctions::
)

Здесь мы используем команду export() для генерации целевых объектов экспорта для дерева сборки. В этом случае мы создадим файл под названием MathFunctionsTargets.cmake в подкаталоге cmake каталога сборки. Сгенерированный файл содержит необходимый код для импорта целевого объекта и может загружаться сторонним проектом, который знает о дереве сборки проекта. Этот файл специфичен для дерева сборки и не перемещаем.

Можно создать соответствующий файл конфигурации пакета и файл версии пакета для определения пакета для дерева сборки, который может использоваться без установки. Потребители дерева сборки могут просто убедиться, что CMAKE_PREFIX_PATH содержит каталог сборки или установить MathFunctions_DIR в <build_dir>/MathFunctions в кэше.

Пример применения этой возможности — построение исполняемого файла на платформе хоста при кросс-компиляции. Проект, содержащий исполняемый файл, может быть построен на платформе хоста, а затем проект, который компилируется для другой платформы, может загрузить его.

Создание и установка пакета

На этом этапе мы сгенерировали перемещаемую конфигурацию CMake для нашего проекта, которую можно использовать после установки проекта. Попробуем собрать проект MathFunctions:

mkdir MathFunctions_build
cd MathFunctions_build
cmake ../MathFunctions
cmake --build .

В каталоге сборки обратите внимание, что файл MathFunctionsTargets.cmake был создан в подкаталоге cmake.

Теперь установите проект:

$ cmake --install . --prefix "/home/myuser/installdir"

Создание перемещаемых пакетов

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

При определении интерфейса целевого объекта для EXPORT, имейте в виду, что каталоги включаемых файлов должны указываться как относительные пути к CMAKE_INSTALL_PREFIX, но не должны явно включать CMAKE_INSTALL_PREFIX:

target_include_directories(tgt INTERFACE
  # Wrong, not relocatable:
  $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/TgtName>
)

target_include_directories(tgt INTERFACE
  # Ok, relocatable:
  $<INSTALL_INTERFACE:include/TgtName>
)

Выражение генератора $<INSTALL_PREFIX> может использоваться в качестве заглушки для префикса установки без создания неперемещаемого пакета. Это необходимо, если используются сложные выражения генератора:

target_include_directories(tgt INTERFACE
  # Ok, relocatable:
  $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/TgtName>
)

Это также относится к путям, ссылающимся на внешние зависимости. Не рекомендуется заполнять любые свойства, которые могут содержать пути, такие как INTERFACE_INCLUDE_DIRECTORIES или INTERFACE_LINK_LIBRARIES, с путями, относящимися к зависимостям. Например, этот код может не работать должным образом для перемещаемого пакета:

target_link_libraries(MathFunctions INTERFACE
  ${Foo_LIBRARIES} ${Bar_LIBRARIES}
  )
target_include_directories(MathFunctions INTERFACE
  "$<INSTALL_INTERFACE:${Foo_INCLUDE_DIRS};${Bar_INCLUDE_DIRS}>"
  )

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

В идеале такие зависимости должны использоваться через собственные IMPORTED целевые объекты, у которых есть собственные свойства IMPORTED_LOCATION и требования к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, заполненные надлежащим образом. Затем эти импортированные целевые объекты можно использовать с командой target_link_libraries() для MathFunctions:

target_link_libraries(MathFunctions INTERFACE Foo::Foo Bar::Bar)

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

Использование файла конфигурации пакета

Теперь мы готовы создать проект для использования установленной библиотеки MathFunctions. В этом разделе мы будем использовать исходный код из Help\guide\importing-exporting\Downstream. В этом каталоге есть исходный файл под названием main.cc, который использует библиотеку MathFunctions для вычисления квадратного корня от заданного числа и затем выводит результаты:

// A simple program that outputs the square root of a number
#include <iostream>
#include <string>

#include "MathFunctions.h"

int main(int argc, char* argv[])
{
  if (argc < 2) {
    std::cout << "Usage: " << argv[0] << " number" << std::endl;
    return 1;
  }

  // convert input to double
  const double inputValue = std::stod(argv[1]);

  // calculate square root
  const double sqrt = MathFunctions::sqrt(inputValue);
  std::cout << "The square root of " << inputValue << " is " << sqrt
            << std::endl;

  return 0;
}

Как и прежде, мы начнём с команд cmake_minimum_required() и project() в файле CMakeLists.txt. Для этого проекта мы также укажем стандарт C++.

cmake_minimum_required(VERSION 3.15)
project(Downstream)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Можно использовать команду find_package():

find_package(MathFunctions 3.4.1 EXACT)

Создайте исполняемый файл:

add_executable(myexe main.cc)

И подключите библиотеку MathFunctions:

target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

Всё! Теперь давайте попробуем скомпилировать проект Downstream.

mkdir Downstream_build
cd Downstream_build
cmake ../Downstream
cmake --build .

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

CMake Warning at CMakeLists.txt:4 (find_package):
  By not providing "FindMathFunctions.cmake" in CMAKE_MODULE_PATH this
  project has asked CMake to find a package configuration file provided by
  "MathFunctions", but CMake did not find one.

  Could not find a package configuration file provided by "MathFunctions"
  with any of the following names:

    MathFunctionsConfig.cmake
    mathfunctions-config.cmake

  Add the installation prefix of "MathFunctions" to CMAKE_PREFIX_PATH or set
  "MathFunctions_DIR" to a directory containing one of the above files.  If
  "MathFunctions" provides a separate development package or SDK, be sure it
  has been installed.

Установите CMAKE_PREFIX_PATH в место, где ранее была установлена MathFunctions, и попробуйте снова. Убедитесь, что созданный исполняемый файл работает как ожидается.

Добавление компонентов

Давайте изменим проект MathFunctions для использования компонентов. Исходный код для этого раздела можно найти в Help\guide\importing-exporting\MathFunctionsComponents. Файл CMakeLists для этого проекта добавляет две поддиректории: Addition и SquareRoot.

cmake_minimum_required(VERSION 3.15)
project(MathFunctionsComponents)

# make cache variables for install destinations
include(GNUInstallDirs)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

add_subdirectory(Addition)
add_subdirectory(SquareRoot)

Сгенерируйте и установите файлы конфигурации пакета и версии пакета:

include(CMakePackageConfigHelpers)

# set version
set(version 3.4.1)

# generate the version file for the config file
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${version}"
  COMPATIBILITY AnyNewerVersion
)

# create config file
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
)

# install config files
install(FILES
          "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
          "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

Если COMPONENTS заданы при использовании find_package() вниз по течению, они перечислены в переменной <PackageName>_FIND_COMPONENTS. Мы можем использовать эту переменную для проверки того, что все необходимые целевые компоненты включены в Config.cmake.in. В то же время, эта функция будет действовать как пользовательская check_required_components макрос, чтобы убедиться, что вниз по течению используются только поддерживаемые компоненты.

@PACKAGE_INIT@

set(_MathFunctions_supported_components Addition SquareRoot)

foreach(_comp ${MathFunctions_FIND_COMPONENTS})
  if (NOT _comp IN_LIST _MathFunctions_supported_components)
    set(MathFunctions_FOUND False)
    set(MathFunctions_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
  endif()
  include("${CMAKE_CURRENT_LIST_DIR}/MathFunctions${_comp}Targets.cmake")
endforeach()

Здесь MathFunctions_NOT_FOUND_MESSAGE установлено на диагностику, что пакет не найден из-за указания неверного компонента. Эта переменная сообщения может быть установлена в любом случае, когда переменная _FOUND установлена в False, и будет отображена пользователю.

Директории Addition и SquareRoot аналогичны. Давайте рассмотрим один из файлов CMakeLists:

# create library
add_library(SquareRoot STATIC SquareRoot.cxx)

add_library(MathFunctions::SquareRoot ALIAS SquareRoot)

# add include directories
target_include_directories(SquareRoot
                           PUBLIC
                           "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
                           "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)

# install the target and create export-set
install(TARGETS SquareRoot
        EXPORT SquareRootTargets
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

# install header file
install(FILES SquareRoot.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

# generate and install export file
install(EXPORT SquareRootTargets
        FILE MathFunctionsSquareRootTargets.cmake
        NAMESPACE MathFunctions::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

Теперь мы можем собрать проект, как описано в предыдущих разделах. Чтобы протестировать использование этого пакета, мы можем использовать проект в Help\guide\importing-exporting\DownstreamComponents. Есть два отличия от предыдущего проекта Downstream. Во-первых, нам нужно найти компоненты пакета. Измените строку find_package со:

find_package(MathFunctions 3.4.1 EXACT)

На:

find_package(MathFunctions 3.4 COMPONENTS Addition SquareRoot)

и строку target_link_libraries со:

target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

На:

target_link_libraries(myexe PRIVATE MathFunctions::Addition MathFunctions::SquareRoot)

В main.cc, замените #include MathFunctions.h на:

#include "Addition.h"
#include "SquareRoot.h"

Наконец, используйте библиотеку Addition:

  const double sum = MathFunctions::add(inputValue, inputValue);
  std::cout << inputValue << " + " << inputValue << " = " << sum << std::endl;

Соберите проект Downstream и убедитесь, что он может найти и использовать компоненты пакета.

© 2000–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.29/guide/importing-exporting/index.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API