Руководство по импорту и экспорту
- Введение
- Создание перемещаемых пакетов
- Использование файла конфигурации пакета
- Добавление компонентов
Введение
В этом руководстве мы представим концепцию целей 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.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 является зависимостью 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> можно использовать в качестве заполнителя для префикса установки, не создавая при этом нерелокируемый пакет. Это необходимо, если используются сложные генераторные выражения:
target_include_directories(tgt INTERFACE # Ok, relocatable: $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/TgtName> )
Это также относится к путям, ссылающимся на внешние зависимости. Не рекомендуется задавать свойства, которые могут содержать пути, например INTERFACE_INCLUDE_DIRECTORIES или INTERFACE_LINK_LIBRARIES, путями, относящимися к зависимостям. Например, этот код может не подойти для создания релокируемого пакета:
target_link_libraries(MathFunctions INTERFACE
${Foo_LIBRARIES} ${Bar_LIBRARIES}
)
target_include_directories(MathFunctions INTERFACE
"$<INSTALL_INTERFACE:${Foo_INCLUDE_DIRS};${Bar_INCLUDE_DIRS}>"
)
Указанные переменные могут содержать абсолютные пути к библиотекам и каталогам включаемых файлов, обнаруженные на компьютере, где был создан пакет. В результате будет создан пакет с жестко заданными путями к зависимостям, который невозможно переместить.
В идеале такие зависимости следует подключать через их собственные импортированные цели, для которых соответствующим образом заданы собственные IMPORTED_LOCATION и свойства требований использования, такие как INTERFACE_INCLUDE_DIRECTORIES. Затем эти импортированные цели можно использовать с командой target_link_libraries() для 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
double const inputValue = std::stod(argv[1]);
// calculate square root
double const 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:
double const sum = MathFunctions::add(inputValue, inputValue); std::cout << inputValue << " + " << inputValue << " = " << sum << std::endl;
Соберите проект Downstream и убедитесь, что он находит компоненты пакета и может их использовать.
© 2000–2026 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html