Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Система построения проектов CMake. Учебник.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]