Руководство по импорту и экспорту
- Введение
- Создание переносимых пакетов
- Использование файла конфигурации пакета
- Добавление компонентов
Введение
В этом руководстве мы представим концепцию 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, будь то из каталога сборки, локальной установки или при упаковке.
В следующих разделах мы подробно рассмотрим несколько примеров проектов. Первый проект создаст и установит библиотеку и соответствующие файлы конфигурации и пакетов 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_. Также к сгенерированным целевым объектам IMPORTED распространяются варианты пользовательских свойств, перечисленных в COMPATIBLE_INTERFACE_STRING и других Свойства совместимого интерфейса. Например, в данном случае целевой объект 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 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 ${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> 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)
С этим подходом пакет ссылается на внешние зависимости только по именам 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)
И подключите библиотеку %%%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 и 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(_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:${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–2021 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.22/guide/importing-exporting/index.html