Руководство по импорту и экспорту
- Введение
- Создание переносимых пакетов
- Использование файла конфигурации пакета
- Добавление компонентов
Введение
В этом руководстве мы представим концепцию целевых компонентов 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.31/guide/importing-exporting/index.html