Spec-Zone.ru › CMake 3.23

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

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

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

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

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

Введение

В данном руководстве мы представим концепцию целевых объектов 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 при сборке в конфигурации release, и md.lib при сборке в конфигурации debug.

Экспорт целевых объектов

Хотя целевые объекты 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 проекта. Начните с указания версии 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 и организует его установку в lib/cmake. Файл содержит код, пригодный для использования зависимыми проектами для импорта всех целевых объектов, указанных в команде установки, из дерева установки.

Опция 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(${INSTALL_PREFIX}/lib/cmake/MathFunctionTargets.cmake)
2 add_executable(myexe src1.c src2.c )
3 target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

Строка 1 загружает файл CMake целевого объекта. Хотя мы экспортировали только один целевой объект, этот файл может импортировать любое количество целевых объектов. Их местоположения вычисляются относительно местоположения файла, чтобы дерево установки можно было легко перемещать. Строка 3 ссылается на импортированную библиотеку 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.

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

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

Модуль 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 для нашего проекта, которая может быть использована после установки проекта. Попробуем собрать проект %%%CODE_BLOCK_185%%:

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> generator expression может использоваться как заполнитель для префикса установки без создания непереносимого пакета. Это необходимо, если используются сложные генераторные выражения:

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_LOCATION и требования к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, заполненные должным образом. Затем эти импортированные целевые объекты могут использоваться с помощью команды target_link_libraries() для %%%CODE_BLOCK_204%%:

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

С таким подходом пакет ссылается на внешние зависимости только по именам импортированных целевых объектов. Когда пользователь использует установленный пакет, пользователь выполнит соответствующие команды 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)

И подключаем библиотеку %%%CODE_BLOCK_220%%:

target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

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

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 и %%%CODE_BLOCK_229%%.

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"

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

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

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

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

Spec-Zone.ru

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