Spec-Zone.ru › CMake 3.19

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

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

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

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

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

Введение

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

В оставшихся разделах мы пройдем по набору примеров проектов пошагово. Первый проект создаст и установит библиотеку и соответствующие файлы конфигурации и пакета 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.txt для проекта MathFunctions. Начните с указания версии cmake_minimum_required() и имени проекта project():

cmake_minimum_required(VERSION 3.15)
project(MathFunctions)

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

Создайте библиотеку под названием 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:include>"
)

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

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

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

install(TARGETS MathFunctions
        EXPORT MathFunctionsTargets
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        RUNTIME DESTINATION bin
        INCLUDES DESTINATION include
)

Здесь опция 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 include)

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

install(EXPORT MathFunctionsTargets
        FILE MathFunctionsTargets.cmake
        NAMESPACE MathFunctions::
        DESTINATION lib/cmake
)

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

Опция 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
2
3

Строка 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 lib/cmake
)

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

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

Теперь нам нужно создать собственный файл конфигурации пакета. В данном случае файл 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 является PUBLIC зависимостью от 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> 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 цели, которые имеют свои собственные IMPORTED_LOCATION и свойства требований к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, заполненные надлежащим образом. Затем эти импортированные цели можно использовать с помощью команды target_link_libraries() для MathFunctions:

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)

И свяжите его с библиотекой 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)

# 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 lib/cmake
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
)

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

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

@PACKAGE_INIT@

set(_supported_components Addition SquareRoot)

foreach(_comp ${MathFunctions_FIND_COMPONENTS})
  if (NOT _comp IN_LIST _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:include>"
)

# install the target and create export-set
install(TARGETS SquareRoot
        EXPORT SquareRootTargets
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        RUNTIME DESTINATION bin
        INCLUDES DESTINATION include
)

# install header file
install(FILES SquareRoot.h DESTINATION include)

# generate and install export file
install(EXPORT SquareRootTargets
        FILE MathFunctionsSquareRootTargets.cmake
        NAMESPACE MathFunctions::
        DESTINATION lib/cmake
)

Теперь мы можем скомпилировать проект, как описано в предыдущих разделах. Для тестирования использования этого пакета мы можем использовать проект в 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–2020 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.19/guide/importing-exporting/index.html

Spec-Zone.ru

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