Добавил:
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()
- •Виды конфигураций
- •Выражения генераторов
- •Информационные выражения
- •Логические выражения
- •Преобразующие выражения
- •Вспомогательные выражения
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Интерфейс подключения библиотек
- •Подключение заголовочной библиотеки
- •Частичная подмена стандартной библиотеки
- •Интерфейс подключения библиотек
- •Локализация приложения
- •Установка приложения
- •Инструменты разработки
- •Управление версиями
- •Генерирование документации
- •Упражнения
- •Тест рубежного контроля
- •Проектное задание
- •Заключение
- •Библиография
- •Ответы на тесты

3.2. Boost
Эта программа ожидает один или несколько аргументов
командной строки: «-h», «-v», «-n hчислоi» (или их длинные версии). Если командная строка не соответствует этому формату,
выводится сообщение о правильном использовании команды,
и программа завершается. Иначе, если указан аргумент «-h»,
выводится справка об аргументах. Также выводятся названия
всех указанных в командной строке аргументов, и, если какойто из них задаёт целочисленный аргумент, выводится его значение.
Пусть требуется возможность построения проекта в конфигурациях «Debug», «Release» и т. д. Пусть также необходима возможность выбора сборки со статическими или разделяемыми
библиотеками. Что касается многопоточности, анализ программы показывает, что библиотека Program Options не используется в нескольких потоках, поэтому её потокобезопасность не требуется.
Таким образом, Файл CMakeLists.txt для проекта может
быть следующим:
cmake_minimum_required(VERSION 3.2)
project(ex-boost)
if(BUILD_SHARED_LIBS)
add_definitions(-DBOOST_ALL_DYN_LINK)
else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED OFF)
281

3. Примеры использования пакетов
if(MSVC)
set(Boost_USE_STATIC_RUNTIME ON)
endif()
endif()
find_package(Boost 1.40 REQUIRED program_options)
include_directories(${Boost_INCLUDE_DIRS})
add_definitions(-DBOOST_ALL_NO_LIB)
add_executable(ex-boost ex-boost.cpp)
target_link_libraries(
ex-boost ${Boost_LIBRARIES})
target_compile_features(
ex-boost
PRIVATE
cxx_auto_type
cxx_range_for)
include(correct_vc_static.cmake)
Модуль correct_vc_static.cmake, подключаемый последней командой, имеет такое же содержимое, что и одноимённый
модуль из примера подключения библиотек OpenCV на с. 258.
Здесь перед вызовом команды find_package() (п. 2.11.1)
устанавливаются значения переменных из табл. 3.3. Переменной Boost_USE_STATIC_LIBS присваивается «истина», ес-
282

3.2. Boost
ли специальная переменная BUILD_SHARED_LIBS установлена
в истину. При использовании компилятора Microsoft Visual
C++в этом случае также устанавливается в истину перемен-
ная Boost_USE_STATIC_RUNTIME, чтобы объектный модуль приложения и библиотечный модуль Boost использовали один
и тот же тип стандартных библиотек времени исполнения
(иначе будут ошибки при компоновке). Дополнительно модуль
correct_vc_static.cmake в этом случае устанавливает ключи компилятора, обеспечивающие зависимость компилируемого объектного модуля от статических библиотек. Значение переменной Boost_USE_MULTITHREADED устанавливается в «ложь»
только в статическом варианте сборки, поскольку библиотеки Boost не собираются в разделяемом однопоточном варианте. Значение переменной Boost_USE_DEBUG_RUNTIME подходит
по умолчанию при любом варианте сборки.
По умолчанию в заголовочных файлах Boost включены зависящие от компилятора директивы подключения библиотек
при компоновке («#pragma comment(lib, hимя_библиотекиi)»).
Эти директивы доступны не для всех компиляторов. Чтобы
они не вступали в конфликт с правилами, определяемыми
CMake, они отключаются установкой символа препроцессора
BOOST_ALL_NO_LIB при помощи команды add_definitions().
Однако в случае использования разделяемых библиотек Boost
это имеет побочный эффект: из объявлений функций и классов исчезает объявление __declspec (dllimport) (см. пример
на с. 110). Чтобы избежать этого, при использовании разделяе-
283

3. Примеры использования пакетов
мых библиотек также устанавливается символ препроцессора
BOOST_ALL_DYN_LINK. ∗
3.2.4. Частичная подмена стандартной библиотеки
В процессе развития программного проекта может возникнуть задача по его портированию на некоторую устаревшую архитектуру, для которой не существует современного компилятора языка C++. Это могут быть старые сборки систем на основе ядра Linux (например, на вычислительных кластерах), версии Windows, поддержка которых прекращена со стороны компиляторов и т. д. Одной из задач портирования является замена используемых возможностей стандартной библиотеки, которых нет в составе старых компиляторов. Чтобы избежать их
реализации вручную, можно использовать тот факт, что большинство их сначала появилось в наборе библиотек Boost, прежде чем быть утверждёнными в новом стандарте (новые контейнеры, интеллектуальные указатели, средства многопоточности
и т. д.). Часто интерфейс этих средств мало различается между стандартной библиотекой и Boost: отличия могут быть только в путях к заголовочным файлам и в названиях пространств
имён.
Если стандартная библиотека компилятора включает
встроенную поддержку требуемых возможностей, лучше использовать её. Если же необходимых средств в ней нет, тогда
на систему, где выполняется сборка, можно установить Boost.
Он занимает не так много места, и большинство его библиотек имеет хорошую совместимость с большим набором компи-
284

3.2. Boost
ляторов, в том числе устаревших. Таким образом, задача заключается в определении того, какие стандартные заголовочные файлы из числа требуемых присутствуют в компиляторе,
и при необходимости использовать вместо них заголовочные
файлы и библиотечные модули Boost. Эта задача аналогична одной из тех, которые решаются сценарием configure, генерируемым инструментами Autotools (п. 1.3.2). Чтобы проверить, имеется ли в наличии та или иная возможность в компиляторе,
сценарий генерирует тексты коротких программ и пытается их
скомпилировать.
В CMake подобные задачи решаются при помощи стандартных модулей, поставляемых вместе с инструментом. Целая серия модулей, имена которых начинаются с «Check», предназначена для проверки наличия тех или иных возможностей экспериментальным путём. Один из таких модулей под названием
CheckIncludeFileCXX может быть использован для проверки
возможности подключения заголовочного файла с заданным путём. В модуле определена команда:
check_include_file_cxx(
hимя_файлаi hимя_переменнойifhфлаги_компилятораig)
Она запускает на компиляцию генерируемый файл C
++
с директивой «#include <hимя_файлаi>». Компиляция выполняется при помощи временного файла CMakeLists.txt, в котором цель определяется командой add_executable(). Компиляция выполняется тем набором инструментов, который соответствует выбранному генератору. Результат (истина/ложь)
285

3. Примеры использования пакетов
записывается в переменную с заданным именем, которая создаётся в кэше (для ускорения последующих запусков CMake,
так как при этом каждый раз не нужно выполнять проверку компилируемости кода). Вывод компилятора на тестовом
запуске записывается в файл CMakeOutput.log, находящийся
в подкаталоге с именем, которое хранится в специальной переменной CMAKE_FILES_DIRECTORY (как правило, содержит строку «/CMakeFiles»). Этот подкаталог находится внутри каталога
построения проекта и предназначен для файлов, генерируемых
CMake.
Модуль CheckCXXSourceCompiles решает более общую задачу: он проверяет компилируемость заданного исходного кода
текущим набором инструментов. В нём определена команда:
check_cxx_source_compiles(
hкодi hимя_переменнойi
f
FAIL_REGEX hрегулярное_выражениеig)
Необязательное регулярное выражение может задавать соответствие вывода компилятора, которое будет означать неудачу.
Процессом компиляции, запускаемым этими командами,
можно управлять при помощи переменных (табл. 3.5).
286

3.2. Boost
Таблица 3.5
Переменные, влияющие на компиляцию командами
check_...
Переменная Значение
CMAKE_REQUIRED_FLAGS Дополнительные флаги ком-
пилятора
*
CMAKE_REQUIRED_DEFINITIONS Дополнительные опре-
деления символов препроцессора (в формате
-Dhимяif=hзначениеig)
CMAKE_REQUIRED_INCLUDES Дополнительные пути поис-
ка заголовочных файлов
CMAKE_REQUIRED_LIBRARIES Библиотеки для компонов-
**
ки
*
Также могут передаваться в качестве третьего аргумента команды check_include_file_cxx().
**
Используется только check_cxx_source_compiles().
ПРИМЕР
Пусть требуется скомпилировать программу, которая использует разделяемые интеллектуальные указатели (файл
ex-shared.cpp):
#include "compatibility.h"
#include <iostream>
287

3. Примеры использования пакетов
struct Test
{
int m_n;
Test() : m_n(99) { }
};
int main()
{
my::shared_ptr <Test> ptr(new Test);
std::cout << ptr->m_n << std::endl;
}
В более новых компиляторах шаблон shared_ptr <> определён в пространстве имён std в заголовочном файле <memory>
(если включена совместимость с C++11 или выше). В тех версиях компиляторов, которые были выпущены до утверждения
стандарта C++11, предварительные версии новых возможностей
библиотек определены в файлах <tr1/...> в пространстве
std::tr1 (по названию «Technical Report 1», неформально обозначающему расширения стандартной библиотеки, принятые
в черновом варианте после выхода стандарта C++03). В ещё более
старых версиях этот тип указателей отсутствует в стандартных
заголовках.
Заголовочный файл compatibility.h определяет пространство имён my:
#ifdef HAS_STD_SHARED
288

#include <memory>
namespace my = std;
#elif defined (HAS_TR1_SHARED)
#include <tr1/memory>
namespace my = std::tr1;
#else
#include <boost/shared_ptr.hpp>
namespace my = boost;
#endif
На описание проекта возлагается задача поиска нужных
3.2. Boost
заголовочных файлов и определения символов препроцессора
HAS_STD_SHARED и HAS_TR1_SHARED в нужных ситуациях (файл
CMakeLists.txt):
cmake_minimum_required(VERSION 2.4)
project(ex-shared)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
include(CheckCXXSourceCompiles)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_REQUIRED_FLAGS -std=c++11)
endif()
check_cxx_source_compiles(
"#include <memory>
int main()
289

3. Примеры использования пакетов
{
std::shared_ptr <int> p;
}"
STD_SHARED_EXISTS)
if(STD_SHARED_EXISTS)
message(STATUS "Found std::shared_ptr <>")
add_definitions(-DHAS_STD_SHARED)
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-std=c++11)
endif()
else()
set(CMAKE_REQUIRED_FLAGS "")
include(CheckIncludeFileCXX)
check_include_file_cxx(tr1/memory TR1_SHARED_EXISTS)
if(TR1_SHARED_EXISTS)
message(STATUS "Found std::tr1::shared_ptr <>")
add_definitions(-DHAS_TR1_SHARED)
else()
find_package(Boost 1.30)
if(Boost_FOUND)
message(STATUS "Found boost::shared_ptr <>")
include_directories(${Boost_INCLUDE_DIRS})
290
else()
message(
FATAL_ERROR
"Neither std::shared_ptr, std::tr1::shared_ptr,"
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
