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

1. Принципы работы систем
автоматического построения
В этой главе мы коротко повторим основные принципы
модульного программирования, которые понадобятся для дальнейшего изложения материала. Также будет приведён обзор основных систем построения проектов, будут рассмотрены их основные достоинства и недостатки в сравнении друг с другом.
1.1. Модульное программирование
При работе над программным проектом разработчик сталкивается со следующими основными проблемами:
— Время компиляции. При увеличении объёма исходного ко-
да растёт и время, требуемое для его компилирования. В со-
ответствии с каскадной моделью процесса разработки ПО
(а также другими моделями) [6], этап кодирования и тести-
рования заключается во внесении изменений в исходный
код, его компиляции, запуске тестовой программы, выявле-
нии ошибок, внесении изменений в исходный код, повтор-
ной компиляции и т. д. Таким образом, вполне возможно,
11

1. Принципы работы систем автоматического построения
что б´ольшую часть рабочего времени разработчики будут
просто ожидать завершения очередной компиляции.
— Организация коллективной работы над проектом. Если
проект требует больших трудозатрат, желательно выделить для его выполнения группу из нескольких человек,
работающих одновременно. Эффективное распределение
задач между ними представляет собой одну из проблем руководства процессом разработки.
— Грамотная организация исходного кода. При увеличении
объёма исходных кодов сверх критической массы вполне
может оказаться так, что разобраться в них будет уже
невозможно даже его авторам. Поэтому данная проблема
также практически всегда рано или поздно встаёт перед
разработчиками.
— Повторное использование собственного и чужого кода.
При разработке алгоритмов, способных быть применёнными не только в текущем программном проекте, но в перспективе и в других, возникает задача их оформления в виде, удобном для повторного использования. Для ранее разработанных алгоритмов, как и для стороннего кода, возникает задача встраивания в текущий проект.
Для решения всех перечисленных задач предназначен подход, называемый модульным программированием (modular pro-
gramming). В соответствии с ним исходный код проекта разделяется на части, называемые исходными, или транслируемыми,
модулями (рис. 1.1). Между модулями устанавливаются зависи-
12

1.1. Модульное программирование
мости по вызываемым подпрограммам, доступу к глобальным
переменным, определённым в других модулях, и т. д. Например,
в языках C/C++средствами межмодульного взаимодействия являются функции и переменные с внешней связью (имеющие в объявлении ключевое слово extern, которое в случае функций подразумевается по умолчанию).
исходные
модули
. . . . . . . . .
*.c
,
*.cpp
компиляция объектные
Рис. 1.1. Схема двухэтапного построения проекта
модули
*.obj
*.o
компоновка
(link)
,
*.lib
*.a
,
исполняемый
модуль
*.exe
*.dll
,
Процедура создания конечного исполняемого модуля при
модульном подходе разделяется на два этапа. На первом из
них для каждого транслируемого модуля независимо от остальных запускается компилятор, который создаёт соответствующий ему объектный модуль. Как правило, объектные модули
представляют собой почти полностью скомпилированные версии исходных модулей, за исключением одной особенности.
При вызове функций и при обращении к переменным в машин-
13

1. Принципы работы систем автоматического построения
ном коде используются адреса этих объектов. При обращении
к функциям и переменным, определённым вне текущего модуля, компилятор не может знать их адресов и поэтому вставляет в объектный модуль ссылки на их символические имена — имена, построенные некоторым образом на основе имён
этих объектов в исходном коде. Каждый объектный модуль содержит таблицу импорта — набор символических имён объектов из других модулей, используемых в текущем. Также объектный модуль содержит таблицу экспорта — набор имён объектов, предоставляемых текущим модулем для использования
извне.
На втором этапе все объектные модули передаются следующему инструменту, называемому компоновщиком (linker), или
редактором связей. Он собирает из них исполняемый модуль,
осуществляя разрешение зависимостей — просмотр всех таблиц
импорта/экспорта и замену ссылок на символические имена реальными адресами (которые ему уже должны быть известны,
поскольку он выполняет размещение в памяти всех функций
и переменных).
Кроме объектных модулей компоновщику также могут
передаваться библиотеки, как правило, представляющие собой несколько объектных модулей, объединённых для удобства
в один файл. Именно так организованы стандартные библиотеки, которые неявно используются компоновщиком.
При помощи разбиения исходного кода на модули решаются задачи организации коллективной работы (разным разработчикам поручается реализация разных модулей), грамот-
14

1.2. Автоматизация построения проектов
ной организации исходного кода и его повторного использования. Но как решается задача ускорения компиляции? Описанная процедура двухэтапного построения (build) не быстрее (а часто и медленнее) обычного. Иногда даже используется приём,
называемый монолитным построением (monolithic build), когда все транслируемые модули подключаются из одного при помощи директив #include и компилятору передаётся этот единственный «модуль».
Однако описанный здесь процесс двухэтапного построения может сочетаться с методом, который иногда называется
инкрементным построением (incremental build1). Согласно ему
при внесении изменений в какие-то транслируемые модули
для повторного построения необходимо заново перекомпилировать только эти изменённые модули, а также, возможно, модули, от них зависящие. При этом если в модуле был изменён только внутренний код каких-то функций, но не их заголовки, зависимые модули в перекомпиляции не нуждаются. Изменение заголовка функции означает изменение машинного кода для её
вызова, и если оно не произошло, то и перекомпилировать использующий её код не нужно. Работа компоновщика, как правило, осуществляется гораздо быстрее сложного процесса компиляции, поэтому здесь может быть достигнута значительная
экономия времени.
1
В инженерии программного обеспечения этот термин может употребляться в ином
смысле.
15

1. Принципы работы систем автоматического построения
1.2. Автоматизация построения проектов
Описанное двухэтапное построение может быть реализовано и вручную, однако на практике это очень неудобно. Автоматически этот процесс может быть реализован на основе сравнения даты изменения исходного и зависимого файлов.
Замечание: для реальных программных проектов схема постро-
ения2может быть более общей, чем изображённая на рис. 1.1.
Одни файлы могут создаваться из других при помощи некоторых инструментов (не обязательно компиляторов). Например, приложения, использующие набор библиотек Qt, могут вызывать его инструменты, генерирующие промежуточный код
на C++[1; 8]. Вместо одного исполняемого модуля может создаваться несколько конечных целей. Например, при помощи
различных инструментов может генерироваться файл справки или документация к коду. Концепция автоматизированного двухэтпаного построения легко распространяется и на такие
случаи.
N
Таким образом, описание построения программного проекта должно включать в себя информацию о следующих его компонентах:
Входные файлы: исходные файлы проекта (транслируемые мо-
дули, графические изображения для элементов пользова-
2
В различных русскоязычных источниках термин «сборка» иногда употребляется как
синоним используемого в настоящем учебнике слова «построение», а иногда — вместо
слова «компоновка».
16

1.2. Автоматизация построения проектов
тельского интерфейса и т. д.), которые создаются и редактируются разработчиком.
Промежуточные файлы: вспомогательные файлы, создавае-
мые в процессе построения проекта (объектные модули
и т. д.).
Выходные файлы: результирующие файлы, представляющие
собой конечный результат построения (исполняемые модули, библиотеки и т. д.).
Правила вызова инструментов (или просто правила): описа-
ние того, какой внешний инструмент (компилятор, компоновщик и т. д.) с какими аргументами командной строки
должен вызываться для получения одних файлов из других.
Цель: файл, создаваемый в результате исполнения правила
(промежуточный или выходной).
Зависимости проекта (или просто зависимости): описание то-
го, какие промежуточные и выходные файлы проекта зависят от входных, промежуточных и выходных файлов и какие правила должны быть применены для создания этих
файлов. Таким образом, зависимости определяют ациклический граф с вершинами, соответствующими файлам проекта, и дугами, соответствующими правилам. Инструмент
построения должен уметь сравнивать время изменения
сгенерированного файла с временем изменения файлов,
от которых он зависит. Если время изменения какого-либо из них окажется позже времени изменения зависимого
17

1. Принципы работы систем автоматического построения
файла (или он ещё вообще не существует), он должен быть
создан заново применением соответствующего правила.
Описание проекта: описание зависимостей проекта для ин-
струмента построения в одном или нескольких файлах.
Инструмент автоматического построения должен уметь
выполнять две операции:
Инкрементное построение: процесс создания всех выходных
файлов проекта с применением правил только при необходимости (с учётом времени изменения файлов и их существования).
Полное построение (перестроение): применение всех описан-
ных правил для создания выходных файлов без учёта времени изменения файлов.
Последняя операция может быть полезна в ряде случаев,
например:
— Изменение системного времени на компьютере, на кото-
ром выполняется построение, из-за чего механизм проверки времени изменения файлов перестаёт быть надёжным
средством определения необходимости применения правил.
— Проверка повторяемости построения проекта с выявлени-
18
ем некоторых типичных ошибок. Например, из проекта
могут быть ошибочно удалены некоторые исходные файлы, в то время как созданные ранее по ним объектные модули до сих пор существуют на диске и используются при

1.2. Автоматизация построения проектов
инкрементном построении выходных файлов. Попытка построить проект на другом компьютере в этом случае, конечно, будет неудачной.
Ещё одной возможностью, которую предоставляют некоторые инструменты построения, является построение вне ка-
талога проекта (out-of source build). Эти инструменты дают
возможность выполнять построение таким образом, чтобы все
промежуточные и выходные файлы создавались в так называемом каталоге построения, отдельном от каталога исходных
файлов проекта (каталога проекта). При этом каталог исходных файлов не «захламляется» большим количеством генерируемых файлов, обычно занимающих очень много места.
В завершение обсуждения автоматизации построения
осталось только рассмотреть вопрос, каким образом средства
построения могут определить транслируемые модули, зависимые от изменённого, которые также нужно перекомпилировать. Для этого рассмотрим следующий пример.
ПРИМЕР
Пусть в проекте имеется исходный модуль a.cpp на языке C++,
в котором определена экспортируемая функция f():
void f()
{
// ...
}
19

1. Принципы работы систем автоматического построения
Пусть в проекте также имеется исходный модуль b.cpp,
который вызывает функцию f(), импортируемую из модуля a.cpp:
// ...
void g()
{
// ...
f();
// ...
}
По этим исходным модулям при построении создаются объектные модули a.o и b.o.
Пусть теперь разработчик изменил заголовок функции f(), например добавив один параметр:
void f(int n = 0)
{
// ...
}
Теперь для построения проекта нужно перекомпилировать модули a.cpp и b.cpp. Возможны два варианта:
1) Разработчик использовал повторное объявление функции f() непосредственно в модуле b.cpp:
void f();
20
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
