Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

Глава 9. Подготовка и выполнение программ

453

рых невозможно было бы получить новую версию целевого файла.

Формат файла описаний зависимостей модулей. Файл описаний зависимостей модулей представляет собой текстовый файл, содержащий последовательность строк, которые являются набором спецификаций взаимозависимостей, используемых командой make при ее выполнении. Спецификация взаимозави­ симостей имеет следующую структуру:

имя целевого файла;

последовательность имен файлов, от которых зависит це­ левой файл;

последовательность команд UNIX, которая должна быть выполнена, если дата последней модификации хотя бы од­

ного из файлов, от которых зависит целевой файл, старше даты модификации целевого файла.

Формат спецификации взаимозависимостей следующий:

targetl[ target2 . . . ]:[:][depend 1 .. .][# ...] [(tab)commands] [#]

где (квадратные скобки означают необязательность элементов)

ta rg e t...

-

целевые файлы, имена которых разделяются

 

 

пробелами;

 

: (или ::)

-

разделитель - в первом случае (для ':') после­

 

 

дующая

последовательность

команд UNIX

 

 

(commands) должна содержаться в одной

 

 

строке файла описаний, а во втором она мо­

 

 

жет располагаться в нескольких строках;

depend ...

-

последовательность имен файлов, от которых

 

 

зависит целевой файл (разделяются пробела­

 

 

ми);

 

 

(tab)

-

символ

"Табуляция", которым

предваряются

 

 

командные строки UNIX;

 

commands -

команды UNIX, с помощью которых должен

 

 

быть получен целевой файл;

 

#- признак начала комментария (комментарий -

часть строки от символа до конца строки).

454

Программирование на языке Си

Примечание. Если список имен файлов, от которых зависит целевой файл, не умещается на одной строке, для обозначе­ ния переноса части списка на другую строку можно восполь­ зоваться символом 'V, например:

tree: tree.о \ add_node.o \ ne\v_node.o \ print.o

команды UNIX

Формат команды make. Команда make имеет следующий формат:

make [-f таке/11е][ключи\[имена\[макро_определения\

Квадратные скобки, выделяющие параметры, означают их необязательность. Параметры в команде отделяются друг от друга пробелами.

Первый параметр с ключом - f задает имя файла зависимо­ стей модулей, если это имя отлично от makefile или Makefile.

Из множества ключей, определяющих режим работы коман­ ды make, упомянем следующие, необходимые для отладки wafe-файла:

-р - вывести в стандартный поток вывода полный список зависимостей модулей;

-i - игнорировать коды возврата выполненных команд (позволяет отладить сложный make-фанп);

-п - вывести в стандартный поток вывода строки с коман­ дами UNIX, не выполняя их.

Параметр имена позволяет задавать имена целевых файлов. Коротко остановимся на некоторых возможностях команды

make, которые не используются в нашем примере.

Макроопределения. Команда make позволяет использовать при записи спецификаций взаимозависимостей макроопределе­ ния. Это дает возможность добиться большей наглядности фай­ ла описаний взаимозависимостей и использовать один и тот же файл описаний для различных имен целевых файлов и файлов, от которых зависит целевой файл.

Глава 9. Подготовка и выполнение программ

455

Макроопределения записываются в соответствии со сле­ дующим форматом:

имямакроса = значение

где имя макроса - имя макроса команды make;

значение - строка символов, которая подставляется вме­ сто конструкции %(имя_макроса) при ее использовании в стро­ ках файла взаимозависимостей.

Пример макроопределения:

СС=сс

После такого макроопределения контекст вида $(СС) будет заменен в строках таке-файла на сс.

Введем в качестве первой строки в приведенный выше файл описаний взаимозависимостей для целевого файла tree следую­ щее макроопределение:

OBJECTS=tree.о add_node.о new_node.о print.о

Тогда позже в том же файле для указания объектных моду­ лей, перечисленных справа от знака равенства, можно приме­ нять конструкцию $(OBJECTS).

Теперь файл взаимозависимостей для программы сортировки на основе бинарного дерева будет выглядеть так:

OBJECTS=add_node.о new_node.о print.о

tree: tree.о $ (OBJECTS)

сс tree tree.с S(OBJECTS)

Встроенные правила. В процессе выполнения команда make использует набор так называемых встроенных правил. Одним из подмножеств этого набора являются правила автома­ тического установления взаимосвязей между файлами по суф­ фиксам их имен. Например, когда в команде make в качестве параметра задано имя файла с суффиксом '.с', автоматически выполняется вызов компилятора языка Си, который строит ис­ полняемый модуль из исходного модуля, находящегося в задан­ ном файле. Таблицу встроенных правил команды make, соответствующих конкретной реализации UNIX, можно найти в

456

Программирование на языке Си

документации по операционной системе. Здесь стандартные встроенные правила мы рассматривать не будем.

Программист может изменить встроенное правило, для чего новый вариант правила необходимо включить в файл описаний зависимостей, т.е. во входные данные для make.

Простейшее правило, описывающее процедуру подготовки исполняемого модуля из исходного модуля (расширение имени 'с', суффикс имени '.с'), будет выглядеть так:

. с :

сс

$@

$*.с

где

$@

- внутренний макрос команды make, предназначен­

ный для спецификации полного имени целевого файла;

 

$*

- внутренний макрос, определяющий префикс име­

ни файла.

Это правило, включенное в файл описаний зависимостей мо­ дулей, заменит внутреннее правило команды make для суффик­ са '.с'

Если теперь ввести команду make с указанием имени целе­ вого файла (исполняемой программы)

%mak.e prog

то исходный файл prog.c будет скомпилирован, а исполняемый модуль получит имя prog.

С помощью команды make может быть решено множество задач, связанных с программированием как на языках высокого уровня, так и на командных языках, например, на командном языке UNIX (sh, csh, ksh,...). Однако основное применение make - учет взаимозависимостей между исходными текстами модулей в больших программных комплексах.

9.1.2. Библиотеки объектны х модулей

При разработке программной системы объектные модули функций, входящих в ее состав, целесообразно размещать в библиотеках объектных модулей, а в командной строке вызова компилятора указывать эти библиотеки. Использование библио-

Глава 9. Подготовка и выполнение программ

457

тек позволяет не задавать непосредственно в командной строке все необходимые для построения исполняемой программы объ­ ектные модули. Компоновщик на этапе построения исполняе­ мой программы будет выбирать из библиотеки только те объек­ тные модули функций, которые необходимы.

Стандартные библиотеки. Стандартные библиотеки UNIX хранятся в каталогах /ИЬ или /usr/lib. Ссылка на библиотеки осуществляется при помощи ключа компоновщика -1, который задается в команде сс вызова компилятора языка Си после всех ключей и параметров компилятора. Непосредственно за ключом (без пробела) указывается идентификатор библиотеки, например

%с с

. . . .

- 1 х

где х -

часть имени библиотеки (полное имя библиотеки в дан­

ном случае libx.a; стандартный префикс для библиотеки "lib"; стандартное расширение имени для библиотеки 'а'). Обратите внимание, что libx.a есть название библиотеки, а не отдельного модуля, поэтому суффикс '.а' не обозначает "модуль на ассемб­ лере", как, например, на рис. 9.2.

Стандартная библиотека языка Си просматривается компо­ новщиком автоматически, т.е. указание этой библиотеки в ко­ мандной строке вызова компилятора Си не требуется. Эта биб­ лиотека хранится в файле /НЬ/ИЬс.а.

Библиотека математических функций хранится в файле /НЬ/ПЬш.а, поэтому указание библиотеки в команде вызова ком­ пилятора будет таким: -1т. Напомним, что ключ -1, являющий­ ся ключом компоновщика, необходимо разместить в командной строке вызова компилятора Си после всех других ключей и па­ раметров, так как просмотр библиотеки должен происходить после компиляции, когда становится известно, какие функции из библиотеки нужны для построения исполняемого модуля.

Создание и сопровождение собственных библиотек. В UNIX для создания и сопровождения библиотек объектных мо­ дулей служит программа ar (atchivator - архиватор). Для биб­ лиотек в UNIX принят термин "архив"; отсюда в качестве расширения имени библиотеки используется буква 'а'.

458

Программирование на языке Си

При создании библиотеки необходимо иметь в виду, что компоновщик просматривает библиотечный файл только один раз. Если объектный модуль функции ссылается на другие име­ на из той же библиотеки, то он должен быть расположен в биб­ лиотеке до этих объектных модулей. Некоторые версии UNIX содержат программу ranlib, которая создает оглавление биб­ лиотеки, что позволяет компоновщику Id обращаться к элемен­ там библиотеки в произвольном порядке. В других версиях UNIX (например, UNIX System V) функция построения оглав­ ления библиотеки встроена в программы аг и Id.

Главное назначение программы аг - создание и обновление библиотечных файлов, используемых компоновщиком, однако ее можно применять и для любых других аналогичных целей. Формат команды аг:

аг -ключ [ключ...] [позиционное имя] архив имя. . .

Параметры в команде отделяются пробелами.

Параметр позиционное имя не является обязательным. Клю­ чей может быть несколько, тогда они записываются без пробе­ лов и только с одним знаком

Параметр архив задает имя архивного файла.

Параметр имя... является списком имен объектных модулей, которые либо находятся в архиве, либо должны быть туда по­ мещены.

Ключи программы аг имеют следующий смысл:

-d -исключить указанные (с помощью параметра имя...) файлы из архивного файла;

-г -заменить указанные (параметром имя...) файлы в ар­ хивном файле. Если вместе с ключом г задается необя­ зательный ключ и, то заменяются только файлы, имеющие более поздние даты модификации, чем фай­ лы в архиве. Этот же ключ используется для включе­ ния в библиотеку новых файлов, т.е. файлы, указанные параметром имя... и отсутствующие в библиотеке, до­ бавляются в архив. Обычно новые файлы помещаются

вконец архива. Если указать имя файла из архива в

Глава 9. Подготовка и выполнение программ

459

параметре позиционное имя, то можно новые файлы поместить до или после этого файла. Для этого необ­ ходимо соответственно добавить к ключу -г необяза­ тельные литеры b (before) или a (after);

-t - вывести в стандартный поток вывода оглавление ар­ хивного файла. При задании параметра имя... печата­ ется информация только об указанных этим парамет­ ром файлах. Этот же ключ используется для включе­ ния в библиотеку новых файлов, т.е. файлы, указанные параметром имя... и отсутствующие в библиотеке, до­ бавляются в архив;

-р - вывести в стандартный поток вывода указанные (параметром имя...) файлы из архива;

-х - извлечь из архива указанные (параметром имя...) фай­ лы. Если не задан параметр имя..., из архива извлека­ ются все файлы. В любом случае собственно архивный файл не изменяется;

-v - выдавать пояснительные сообщения;

-с - создать архивный файл. Обычно программа аг при необходимости создает архивный файл сама. Данный ключ подавляет информационное сообщение, выда­ ваемое при создании архивного файла.

Для размещения в личной библиотеке объектных модулей функций программы сортировки на основе бинарного дерева (из гл. 8) необходимо выполнить следующую команду:

%ar -rv libtree.a add_node.o new_node.o print.о

Здесь libtree.a - имя библиотеки (архивного файла).

Ключ -г задает режим замены объектных модулей, храня­ щихся в библиотеке, на объектные модули, список которых приведен в командной строке вызова архиватора аг после имени библиотеки. Так как в момент обработки указанной команды библиотеки не существовало, она будет создана автоматически. Ключ -v определяет режим вывода пояснительных сообщений о работе архиватора аг.

460

Программирование на языке Си

После занесения объектных модулей в библиотеку необхо­ димо при помощи программы ranlib скорректировать (или соз­ дать) оглавление библиотеки, выполнив команду

%ranlib libtree.a.

Программа ranlib впервые появилась в версии 7 UNIX фир­ мы AT&T и существует в таких реализациях операционной системы UNIX, как FreeBSD и Solaris. Рекомендуется при ра­ боте в других реализациях UNIX ознакомиться со справочной литературой, в которой описывается работа с библиотеками. Наиболее простой способ получения информации о наличии программы ranlib в составе ОС состоит в поиске этой програм­ мы в каталогах /bin, /usr/bin, /etc. Если эта программа найдена, прочтите ее описание, которое можно получить на экране дис­ плея по "самой полезной" команде ОС UNIX - команде man, позволяющей быстро получить справку по многим компонен­ там, командам и программам UNIX. Например, справку о про­ грамме ranlib можно получить так:

%man ranlib

Проверим созданную библиотеку, распечатав ее оглавление:

%ar -t libtree.a __ .SYMDEF

add_node.о new_node.о print.о

Первая строка - вызов программы-архиватора (аг). Послед­ ние 4 строки - это результат работы команды аг. Оглавление библиотеки содержится в разделе библиотеки__.SYMDEF. Ос­ тальные строки - это имена объектных модулей, находящихся в библиотеке. После того как библиотека объектных модулей создана, можно построить исполняемый модуль программы сортировки на основе бинарного дерева с помощью команды:

%сс -о tree tree.с -ltree

Здесь транслируется головной модуль (tree.с), объектные мо­ дули на этапе компоновки выбираются из библиотеки libtree.a, и

Глава 9. Подготовка и выполнение программ

461

строится исполняемая программа с именем tree. Ключ компо­ новщика -1 позволяет задать имя библиотеки объектных моду­ лей (полное имя библиотеки: Iibtree.a).

Предположим, что в исходный текст одной из функций (например, add_node()) были внесены изменения. Тогда для по­ строения обновленного варианта исполняемой программы не­ обходимо выполнить следующие команды:

%сс

add_node.c

add_node.о

%ar

-rv

libtree.a

%сс

tree

tree.о

-ltree

На этот раз нет необходимости в повторной трансляции го­ ловного модуля (он не подвергался правке), поэтому в послед­ ней строке задано имя уже существующего объектного модуля tree.о.

Применим в той же задаче сортировки на основе бинарного дерева команду make для поддержания личной библиотеки объ­ ектных модулей в таком состоянии, когда она всегда содержит объектные модули, полученные из последних версий соответст­ вующих исходных модулей. Выше был приведен пример файла зависимостей команды make, в котором все объектные модули, используемые для построения исполняемого модуля программы сортировки, указывались в командной строке вызова компиля­ тора. В варианте таке-§гмп&, ориентированном на применение личной библиотеки объектных модулей, появляются 2 целевых файла: исполняемая программа сортировки (tree) и библиотека объектных модулей (Iibtree.a), причем исполняемая программа зависит от содержимого (объектных модулей) библиотеки. Взаимозависимость этих объектов определяется следующим образом:

tree: tree.о \ add_node.о \ new node.о \ print.о \ libtree.а

Iibtree.a: libtree.a(add_node.о) \

1 i b t r e e . а (n e w _ n o d e . о ) \

libtree.a(print.о)

462

Программирование на языке Си

Обратите внимание, как указаны зависимость целевого файла tree от библиотеки Iibtree.a и, в свою очередь, ее зависимость от объектных модулей. Объектный модуль из библиотеки Iibtree.a указывается так:

имя_библиотеки(имямодуля).

Для окончательного оформления таке-$гмп& используем два встроенных макроса команды make:

$@ - задает имя текущего целевого файла (в нашем случае - последнего (Iibtree.a));

$? - значение этого макроса вычисляется командой make - это имена всех модулей, которые подвергались изменениям.

Приведем один из вариантов полного текста таке-файла (напомним, что его имя по умолчанию - makefile), позволяю­ щего обновлять целевые файлы (исполняемую программу и библиотеку объектных модулей) в соответствии с изменениями в исходных текстах функций, входящих в состав программы сортировки на основе бинарного дерева:

tree: tree.о \ add_node.о \ new_node.о \ print.о \ libtree.а

Iibtree.a: libtree.a(add_node.о) \ libtree.a(new_node.о) \ libtree.a(print.о)

ar

-rv

$@

$?

 

ranlib

$0

tree.с

-ltree

сс

tree

Теперь для того, чтобы построить исполняемый модуль, учи­ тывающий все внесенные изменения в любые функции про­ граммы сортировки, необходимо просто набрать на клавиатуре команду make.

После внесения изменений, например, в исходный текст функции new_node() и при выполнении команды make получим на экране дисплея следующий протокол:

Соседние файлы в папке книги