С.В. Герасимов, И.В. Машечкин, М.И. Петровский и др. - Инструментальные средства разработки ПО в ОС UNIX (1114658), страница 5
Текст из файла (страница 5)
Вызовы функций рассматриваютсякак одна инструкция, то есть вызванные функции выполняются вобычном, а не пошаговом режиме.s (или step) — сделать указанное количество шаговвыполнения программы (по умолчанию 1). При вызовах функцийвыполнение входит в функции в пошаговом режиме.Сборка программы с помощью GNU MakeПрограммаmakeпредназначенадляотслеживаниязависимостей одних файлов от других файлов, выявления«устаревших» файлов при помощи сравнения времен модификациифайлов и выполнения команд для «обновления» устаревших файлов.Make является представителем систем автоматизации сборки(англ., build automation system), позволяющих автоматизироватьпроцессы трансляции, компоновки, запуска модульных тестов иразвертывания системы за счет описания соответствующихсценариев на специальном языке.Сценарии Make описываются в т.н.
файле проекта. Проектомназывается совокупность файлов, зависящих друг от друга. Файлописания проекта перечисляет зависимости между файлами и задаеткоманды для обновления зависимых файлов. Имя файла описанияпроекта задается опцией –f командной строки программы make и поумолчанию предполагается равным Makefile или makefile.
Если имяфайла проекта явно не задано, при запуске утилита ищет в текущемкаталоге файл с указанными выше именами, и, если такой файлсуществует, выполняет команды из него.Таким образом, в простейшей форме командаmake24по описанию проекта в файле Makefile или makefile определяет,какие файлы устарели и нуждаются в обновлении и запускаетсоответствующие команды.Форма описания проектов в файле Makefile достаточно сильноварьируется от версии к версии. Утилита make на системах FreeBSDплохо совместима с утилитой GNU Make, которая на FreeBSDобычно называется gmake.
В данном разделе будет рассматриватьсяименно GNU make (http://www.gnu.org/software/make/).Одно из применений утилиты make – отслеживаниезависимостей между исходными и объектными файлами в проектахна языке Си или Си++ и управление компиляцией программы – тоесть сборка проекта. Использование make с этой цельюрассматривается в данном разделе.Обычно программы на языках Си или Си++ представляютсобой совокупность нескольких .c (.cpp) файлов с реализациямифункций и .h файлов с прототипами функций и определениямитипов данных. Как правило, каждому .c файлу соответствует .h файлс тем же именем.Предположим, что разрабатываемая программа называетсяearth и состоит из файлов arthur.c, arthur.h, trillian.c, trillian.h,prosser.c, prosser.h.
Разработка программы ведется в POSIX-среде сиспользованием компилятора GCC.Простейший способ скомпилировать программу — указать всеисходные .c файлы в командной строке gcc:gcc arthur.c trillian.c prosser.c -o earthКомпилятор gcc выполнит все этапы компиляции исходныхфайлов программы и компоновку исполняемого файла earth.Обратите внимание, что в командной строке gcc указываются только.c файлы и никогда не указываются .h файлы.Компиляция и компоновка при помощи перечисления всехисходных файлов в аргументах командной строки GCC допустималишь для совсем простых программ.
С ростом числа исходныхфайлов ситуация очень быстро становится неуправляемой. Крометого, каждый раз все исходные файлы будут компилироваться отначала до конца, что в случае больших проектов занимает много25времени. Поэтому обычно компиляция программы выолняется в дваэтапа: компиляция объектных файлов и компоновка исполняемойпрограммы из объектных файлов. Каждому .c файлу теперьсоответствует объектный файл, имя которого в POSIX-системахимеет суффикс .o. Таким образом, в рассматриваемом случаепрограмма earth компонуется из объектных файлов arthur.o, trillian.oи prosser.o следующей командой:gcc arthur.o trillian.o prosser.o -o earthКаждый объектный файл должен быть полученсоответствующего исходного файла следующей командой:изgcc -c arthur.cОбратите внимание, что явно задавать имя выходного файланеобязательно. Оно будет получено из имени компилируемогофайла заменой суффикса .c на суффикс .o. Итак, для компиляциипрограммы earth теперь необходимо выполнить четыре команды:gccgccgccgcc-c arthur.c-c trillian.c-c prosser.carthur.o trillian.o prosser.o -o earthХотя теперь для компиляции программы необходимо выполнитьчетыре команды вместо одной, взамен получаются следующиепреимущества:если изменение внесено в один файл, например, в файлprosser.c, нет необходимости перекомпилировать файлыtrillian.o или arthur.o; достаточно перекомпилировать файлprosser.o, а затем выполнить компоновку программы earth;компиляция объектных файлов arthur.o, trillian.o и prosser.o независит друг от друга, поэтому может выполнятьсяпараллельнонамногопроцессорном(многоядерном)компьютере.В случае нескольких исходных .c и .h файлов исоответствующих промежуточных .o файлов отслеживать, какойфайл нуждается в перекомпиляции, становится сложно, и здесь напомощь приходит программа make.
По описанию файлов и команддля компиляции программа makе определяет, какие файлы26нуждаются в перекомпиляции, и может выполнять перекомпиляциюнезависимых файлов параллельно.Файл A зависит от файла B, если для получения файла Aнеобходимо выполнить некоторую команду над файлом B. Можносказать, что в программе существует зависимость файла A от файлаB. В нашем случае файл arthur.o зависит от файла arthur.c, а файлearth зависит от файлов arthur.o, trillian.o и prosser.o. Можно сказать,что файл earth транзитивно зависит от файла arthur.c.Зависимость файла A от файла B называется удовлетворенной,если:все зависимости файла B от других файлов удовлетворены;файл A существует в файловой системе;файл A имеет дату последней модификации не раньше датыпоследней модификации файла B.Если все зависимости файла A удовлетворены, то файл A ненуждается в перекомпиляции.
В противном случае сначалаудовлетворяются все зависимости файла B, а затем выполняетсякоманда перекомпиляции файла A.Например, если программа earth компилируется в первый раз,то в файловой системе не существует ни файла earth, ни объектныхфайлов arthur.o, trillian.o, prosser.o. Это значит, что зависимостифайла earth от объектных файлов, а также зависимости объектныхфайлов от .c файлов не удовлетворены, то есть все они должны бытьперекомпилированы. В результате в файловой системе появятсяфайлы arthur.o, trillian.o, prosser.o, даты последней модификациикоторыхбудутбольшедатпоследнеймодификациисоответствующих .c файлов (в предположении, что часы накомпьютере идут правильно, и что в файловой системе нет файлов«из будущего»).
Затем будет создан файл earth, дата последнеймодификации которого будет больше даты последней модификацииобъектных файлов.В получившейся конфигурации все зависимости всех файловдруг от друга удовлетворены, и поэтому для компиляции программыearth не нужно выполнять никаких команд.27Предположим теперь, что в процессе разработки был измененфайл prosser.c. Его время последнего изменения теперь большевремени последнего изменения файла prosser.o. Зависимостьprosser.o от prosser.c становится неудовлетворенной, и, какследствие, зависимость earth от prosser.o также становитсянеудовлетворенной.
Чтобы удовлетворить зависимости необходимоперекомпилировать файл prosser.o, а затем файл earth. Файлыarthur.o и trillian.o можно не трогать, так как зависимости этихфайлов от соответствующих .c файлов удовлетворены.Такова общая идея работы программы make и, на самом деле,всехпрограммуправлениясборкойпроекта:ant(http://ant.apache.org/), scons (http://www.scons.org/) и др.Хотя утилита make присутствует во всех системахпрограммирования, вид управляющего файла или набор опцийкомандной строки могут сильно различаться. Далее будетрассматриваться командный язык и опции командной строкипрограммы GNU make.
В дистрибутивах операционной системыLinux программа называется make. В BSD, как правило, программаGNU make доступна под именем gmake.Файл описания проекта может содержать описанияпеременных, описания зависимостей и описания команд, которыеиспользуются для компиляции. Каждый элемент файла описанияпроекта должен, как правило, располагаться на отдельной строке.Для размещения элемента описания проекта на нескольких строкахиспользуется символ продолжения \ точно так же, как в директивахпрепроцессора языка Си.Определения переменных записываются следующим образом:<имя> = <определение>Использование переменной записывается в одной из двух форм:$(<имя>)или${<имя>}Эти формы равнозначны.28Переменные рассматриваются как макросы, то естьиспользование переменной означает подстановку текста изопределения переменной в точку использования.