Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Система построения проектов CMake. Учебник.pdf
X
- •Введение
- •Модульное программирование
- •Автоматизация построения проектов
- •Обзор инструментов построения проектов
- •qmake
- •Генераторы
- •Входные файлы
- •Пути
- •Синтаксис
- •Команды
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Основные концепции
- •Строки
- •Свойства
- •Примеры простых проектов
- •Команды общего назначения
- •cmake_minimum_required()
- •project()
- •include()
- •message()
- •Команды описания целей
- •add_executable()
- •add_library()
- •add_subdirectory()
- •Команды настроек целей
- •include_directories()
- •target_include_directories()
- •add_definitions(), add_compile_options()
- •target_compile_definitions()
- •target_compile_options()
- •target_compile_features()
- •target_link_libraries()
- •add_dependencies()
- •Команды обработки данных
- •math()
- •list()
- •Команды управляющих конструкций
- •foreach(), endforeach()
- •Команды работы с файлами
- •get_filename_component()
- •Команды добавления специальных целей
- •configure_file()
- •add_test(), enable_testing()
- •install()
- •add_custom_target()
- •Прочие команды
- •find_package()
- •get_property(), set_property()
- •Виды конфигураций
- •Выражения генераторов
- •Информационные выражения
- •Логические выражения
- •Преобразующие выражения
- •Вспомогательные выражения
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Интерфейс подключения библиотек
- •Подключение заголовочной библиотеки
- •Частичная подмена стандартной библиотеки
- •Интерфейс подключения библиотек
- •Локализация приложения
- •Установка приложения
- •Инструменты разработки
- •Управление версиями
- •Генерирование документации
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Заключение
- •Библиография
- •Ответы на тесты

2.12. Поддержка нескольких конфигураций построения
}
Описание цели библиотеки выглядит следующим образом
(файл my_plugin/CMakeLists.txt):
add_library(my_plugin MODULE my_plugin.cpp)
Теперь возникает задача создания теста для библиотеки.
Очевидно, тестовая программа должна загружать библиотеку
по заданному пути к ней, используя для этого, например, функции интерфейса POSIX. Поскольку библиотека и тест могут собираться в разных подкаталогах, надёжным способом ссылки
на библиотеку будет указание полного пути к ней, который
можно передавать программе через аргумент командной строки.
Исходный код теста (файл my_test/my_test.cpp):
#include <dlfcn.h>
#include <iostream>
#include <cstddef>
using namespace std;
int main(int nArgC, char *apszArgV[])
{
if (nArgC < 2)
{
cerr << "Wrong usage" << endl;
return 1;
231

2. Основы языка CMake
} // if (nArgC < 2)
//
void *pvHandle = dlopen(
apszArgV[1], RTLD_LOCAL | RTLD_LAZY);
if (NULL == pvHandle)
{
cerr << "Cannot open library" << apszArgV[1] <<
": " << dlerror() << endl;
return 2;
} // if (NULL == pvHandle)
//
int nRet = 0;
//
typedef int (*PFN_T)();
const char *pcszName = "my_plugin_f";
PFN_T pfn = (PFN_T) dlsym(pvHandle, pcszName);
if (NULL == pfn)
{
cerr << "Cannot get function " << pcszName <<
": " << dlerror() << endl;
nRet = 3;
} // if (NULL == pfn)
else
232
{
const int cn = (*pfn)();
if (cn != 31)
{

2.12. Поддержка нескольких конфигураций построения
cerr << "The return value is different: " <<
cn << endl;
nRet = 4;
} // if (cn != 31)
} // if (NULL == pfn) (else)
//
dlclose(pvHandle);
//
return nRet;
} // main()
Здесь сначала проверяется, что программе был передан хотя бы один аргумент. Если это так, он передаётся функции POSIX
dlopen(), которая выполняет динамическую загрузку модуля
[13]. Если загрузка выполнена успешно, при помощи функции
dlsym() в модуле выполняется поиск адреса функции с именем
«my_plugin_f», который присваивается переменной-указателю
на функцию pfn. Если эта функция найдена, выполняется её косвенный вызов и проверка возвращённого значения.
Описание цели теста (файл my_test/CMakeLists.txt):
add_executable(my_test my_test.cpp)
target_link_libraries(my_test -ldl)
Здесь цель связывается с системной библиотекой libdl, которая экспортирует функции dlopen(), dlsym() и т. д.
Описание проекта верхнего уровня имеет следующий вид
(файл CMakeLists.txt):
233

2. Основы языка CMake
cmake_minimum_required(VERSION 2.8)
enable_testing()
add_subdirectory(my_plugin)
add_subdirectory(my_test)
add_test(
NAME my_plugin_test
COMMAND my_test "$<TARGET_FILE:my_plugin>")
Здесь командой add_test() (п. 2.10.2) добавляется
тест, который запускает приложение-результат сборки цели my_test с путём к библиотеке в аргументе командной
строки. Последний получается вычислением выражения
$<TARGET_FILE:my_plugin>. ∗
Замечания:
— В приведённом примере при определении теста невозмож-
но обойтись без выражения генераторов, записав команду
add_test() подобным образом:
add_test(
NAME my_plugin_test
COMMAND my_test my_plugin)
234
Хотя имя цели my_test заменяется на путь к исполняемо-
му файлу, поскольку указано вместо имени команды, имя
my_plugin уже указано в качестве её аргумента. Поэтому

2.12. Поддержка нескольких конфигураций построения
при запуске теста оно будет передано программе в неизменном виде.
— Тестовое приложение можно также реализовать с ис-
пользованием функций Windows API LoadLibrary(),
GetProcAddress() и т. д.
N
2.12.4. Логические выражения
Логические выражения генераторов (табл. 2.15) после вычисления заменяются на «1» или «0» в зависимости от истинности их условия.
Таблица 2.15
Логические выражения генераторов
Выражение Условие
Основные
STREQUAL:hстрока1i,hстрока2i hстрока1i hстрока2i
EQUAL:hчисло1i,hчисло2i hчисло1i hчисло2i
VERSION_EQUAL:hверсия1i,hверсия2i hверсия1i hверсия2i
VERSION_LESS:hверсия1i,hверсия2i hверсия1i < hверсия2i
VERSION_GREATER:hверсия1i,hверсия2i hверсия1i > hверсия2i
BOOL:hстрокаi Истинность/
ложность строки
в соответствии
с табл. 2.1
Составные
235

2. Основы языка CMake
Выражение Условие
NOT:hзначi
AND:hзнач1i(,hзначni
OR:hзнач1i(,hзначni
Продолжение табл. 2.15
0, если hзначi 1
1, иначе
)
1, если ∀k hзначki 1
0, иначе
)
0, если ∀k hзначki 0
1, иначе
Системные
CONFIG:hстрокаi Равенство использу-
PLATFORM_ID:hстрокаi Равенство целевой
C_COMPILER_ID:hстрокаi Равенство имени
CXX_COMPILER_ID:hстрокаi Равенство имени
*
емой конфигурации
строке без учёта регистра
платформы строке
компилятора C строке
компилятора C
++
236
строке

2.12. Поддержка нескольких конфигураций построения
Окончание табл. 2.15
Выражение Условие
C_COMPILER_VERSION:hверсияi Равенство версии
компилятора C заданной
CXX_COMPILER_VERSION:hверсияi Равенство версии
компилятора C
заданной
)
COMPILE_FEATURES:hвозм1i(,hвозмki
*
Эти выражения введены для удобства, их можно заменить
комбинацией нескольких других выражений.
**
При помощи этого выражения можно условно включать
в проект реализацию участков кода на основе новых возможностей компилятора и без их использования, если компиля-
Доступность всех возможностей в используемом компиляторе
(п. 2.6.6)
**
++
тор их не поддерживает (например, при помощи шаблонов
C++11 с переменным количеством параметров или с ограниченным, поддерживаемых C++98).
2.12.5. Преобразующие выражения
Преобразующие выражения (табл. 2.16) вычисляются в за-
висимости от входных аргументов.
237

2. Основы языка CMake
Преобразующие выражения генераторов
Выражение Результат
Таблица 2.16
0:hстрокаi Пустая строка
1:hстрокаi hстрокаi
*
*
LOWER_CASE:hстрокаi hстрокаi в нижнем регистре
UPPERR_CASE:hстрокаi hстрокаi в верхнем регистре
JOIN:hстрока1i,hстрока2i hстрока1i, в которой все вхож-
дения точки с запятой заменены на hстрока2i
**
MAKE_C_IDENTIFIER:hстрокаi hстрокаi, преобразованная
к виду, который может быть
использован в качестве корректного идентификатора
в языке C
***
Системные
LINK_ONLY:hстрокаi Пустая строка, если выраже-
ние вычисляется при определении транзитивных требований (п. 2.6.7), иначе hстрокаi
BUILD_INTERFACE:hстрокаi hстрокаi, если цель использует-
ся другой целью в пределах того же проекта, иначе пустая
строка
238
****

2.12. Поддержка нескольких конфигураций построения
Окончание табл. 2.16
Выражение Результат
INSTALL_INTERFACE:hстрокаi hстрокаi, если цель экс-
портируется командой
install(EXPORT ...), иначе
пустая строка
*
Предполагается, что эти выражения будут использоваться
****
совместно с логическими выражениями (см. табл. 2.15), которые заменяются на 0 или 1 (пример: $<$<CONFIG:debug>:-D
_DEBUG>).
**
Предполагается, что это выражение будет использоваться для обработки свойств целей, представляющих списки
(см. пример ниже).
***
Например, идентификатор CMake «01-hello» будет преобра-
зован к «_01_hello».
****
Предполагается, что эти команды будут использоваться
в командах target_include_directories() (п. 2.6.2) и т. п.
для цели библиотеки, имеющей правило для установки
(install(EXPORT ...), п. 2.10.3).
ПРИМЕРЫ
1)
set(
DEFS
"$<TARGET_PROPERTY:prog,COMPILE_DEFINITIONS>")
239

2. Основы языка CMake
target_compile_options(
prog
PRIVATE "-D $<JOIN:${DEFS}, -D >")
Здесь для удобства строка с выражением разбита на
две: первая в переменной DEFS содержит выражение,
которое при вычислении заменяется на значение свойства COMPILE_DEFINITIONS цели prog. Это свойство содержит список определений символов препроцессора,
заполняемый командами add_definitions() (п. 2.6.3)
и target_compile_definitions() (п. 2.6.4). Внутри команды target_compile_options() (п. 2.6.5) эта строка подставляется внутрь другого выражения JOIN, которое при
вычислении заменяет в этом списке все точки с запятой, разделяющие его элементы, на строки « -D ». Строка «-D » перед выражением добавляется перед первым
определением. Таким образом, всё выражение после вычисления генератором развернётся в список аргументов
формата компилятора gcc, определяющих заданные символы («-D MY_DEF1 -D MY_DEF2 ...») в предположении, что
свойство COMPILE_DEFINITIONS содержит непустой список.
2) Пусть проект приложения реализует некоторые возмож-
240
ности средствами, зависящими от целевой операционной
системы. Например, в Windows он может использовать
Windows API, а в POSIX-совместимых системах — соответ-
ственно API POSIX. Разработчик выделил все зависящие
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
