Производственно-внедренческий кооператив
"И Н Т Е Р Ф Е Й С"
Диалоговая Единая Мобильная
Операционная Система
Демос/P 2.1
Интерпретатор
make
Москва
1988
АННОТАЦИЯ
Описан интерпретатор make, используемый для сборки
пакетов программ, минимизирующий число вызовов транслятора.
1. Введение
Интерпретатор make [1], наиболее часто используемый
программистами ДЕМОС, предоставляет уникальные возможности
по управлению любыми видами работ в операционной системе.
Всюду, где имеется необходимость учитывать зависимости фай-
лов и времена их создания (модификации), make оказывается
незаменимым инструментом. Интерпретатор реализует непроце-
дурный язык, который позволяет управлять группами командных
строк системы. В основу такого управления положены зависи-
мости между файлами с исходными данными и файлами, в которых
содержатся результаты. При этом предполагается любой возмож-
ный список действий над исходными файлами: компиляция, мак-
рообработка, редактирование, печать, упаковка или шифрование
и т.д. Исходной информацией для интерпретатора является
Make-программа, представлящая список определений макропере-
менных и список правил. Каждое правило включает формулировку
цели и список действий для интерпретатора shell. При выпол-
нении Make-программы интерпретатор make использует информа-
цию о связях между целями и результатами и передает на
выполнение shell списки действий, которые в данный момент
необходимо выполнить для получения заданного результата.
Таким образом, интерпретатор make позволяет записывать любой
набор действий над исходными данными, благодаря чему он
широко используется при решении прикладных и общесистемных
задач. Очень важно и то, что Make-программа становится
общесистемным стандартным описанием структуры задачи, алго-
ритма сборки и установки программного комплекса. Програм-
мист, владеющий средствами интерпретатора make, использует
следующую технологию разработки программного комплекса,
независимо от его сложности:
редактор -> make -> проверка -> редактор
При такой технологии существенно повышается производитель-
ность труда программиста,так как он освобождается от ручной
сборки программ, сокращается загрузка ЭВМ - make "следит" за
тем, чтобы при многократных компиляциях и отладках программ
"не делалолсь то, что можно не делать".
Важно отметить, что make является средством автоматиза-
ции процедур установки компонент ДЕМОС. Например, компонента
системы cat может включать следующие файлы:
выполняемый код - файл /bin/cat
текст программы - файл ./src/cmd/cat/cat.c
документацию - файл ./man/man1/cat.1
программу сборки - файл ./src/cmd/cat/Makefile
Файл ./src/cmd/cat/Makefile содержит всю необходимую для
правильной компиляции и установки в ОС компоненты cat инфор-
мацию. Особенно эффективен make для выполнения работ в
программных проектах малой и средней (до 200 файлов)
- 3 -
величин.
2. Принципы выполнения Make-программы
Интерпретатор make выполняет программу, которую будем
называть Make-программой. Make-программа содержит структуру
зависимостей файлов и действий над ними, оформленных в виде
списка правил. Выполнение действий приводит к созданию тре-
буемых файлов. Допустим, имеются файлы
a b c d e f
из которых определенным образом необходимо получить файл E.
Пусть далее известно, что над различными комбинациями исход-
ных файлов выполняются некоторые действия, а результаты
будут размещены в промежуточных файлах A, B, C и D. Расс-
мотрим граф, узлы которого - имена файлов. Дуги графа отра-
жают зависимости файлов, стрелка указывает направление пре-
образований от исходных файлов к файлам, которые необходимо
получить.
В Make-программе каждой паре (x,y) инцидентных узлов
этого графа ставится в соответствие список действий, выпол-
нение которых приведет к созданию x. Когда файл x сущест-
вует, список действий не выполняется, но только в том слу-
чае, если файл y создан (модифицирован) раньше по времени,
чем файл x. Каждой дуге графа можно поставить в соответсвие
значение функции t(x,y). Функция t(x,y) возвращает результат
в виде
/\
цель E / \
| /----\
| ||
t(E,D ) | t(E,C) ||
----------------------- ||
| | ||
цель D цель C ||
t(D,A) | t(D,B) | ||
---------------- | ||
| | | ||
цель A цель B | ||
| | t(C,e)| ||
t(A,a)| t(A,b) t(B,c)| t(B,d) t(C,d)| ||
------------ ------------ ------- ||
| | | | | | ||
| | | | | | ||
a b c d e f ||
- 4 -
x МОЛОЖЕ y - список действий не выполняется;
x СТАРЕЕ y - список действий выполняется.
Множество значений функции t(x,y) образует структуру динами-
ческих (зависящих от времени) связей между файлами. На этой
основе интерпретатор make выделяет те разделы Make-
программы, которые можно не выполнять.
Выше предполагалось, что каждый узел графа - это файл.
Существует возможность записать в Make-программe список
действий, выполнение которых не связано с созданием файла.
Поэтому в общем случае узел графа правильнее называть целью.
Пара инцидентных узлов графа образует цель и подцель. В при-
мере узел E - цель, узлы D и C - подцели цели E. Аналогично
узел D - цель, узлы A и B - подцели цели D. Наконец, узел A
- цель, узлы a и b - подцели узла A. Перечисление вида ЦЕЛЬ
- ПОДЦЕЛИ отражает обобщенную структуру алгоритма достижения
целей.
Введем понятие реконструкция файла-цели. Если файл-цель
существует и "МОЛОЖЕ" всех файлов, от которых зависит, то он
остается без изменений, иначе, если файл-цель существует и
"СТАРЕЕ" какого-либо файла, от которого зависит, он реконст-
руируется (изготавливается заново).
Приведем пример Make-программы, соответствующей приве-
денному выше графу. Программа выглядит как его линейная
запись:
E : D C # E зависит от D и C
cat D C > E # действие правила 1
D : A B # D зависит от A и B
cat A B > D # действие правила 2
A : a b # A зависит от a, b
cat a b > A # действие правила 3
B : c d # B зависит от c, d
cat c d > B # действие правила 4
C : e f # C зависит от e, f
cat e f > C # действие правила 5
clean clear:
-rm -f A B C D E
Здесь содержится 6 правил. Каждое правило включает строку
зависимостей файлов и командную строку системы. Правило
описывается просто: сначала указывается имя файла, который
- 5 -
необходимо создать (цель), затем двоеточие, затем имена фай-
лов, от которых зависит создаваемый файл (подцели), затем
строки действий. Первую строку правила назовем строкой
зависимостей. Следует обратить внимание на шестое правило:
в нем пустой список подцелей в строке зависимостей. Синтак-
сис строк действий соответствует синтаксису командных строк
shell. Первым символом строки действия в Make-программе дол-
жен быть символ табуляции (или 8 пробелов) - это обязатель-
ное условие.
Все последовательности символов, начиная от символа # и
до конца строки, являются комментарием. Пустые строки и лиш-
ние пробелы игнорируются. Если Make-программа размещена в
файле Makefile, то имя файла с Make-программой в командной
строке можно не указывать. Допустим, файл с Make-программой
называется Makefile, в рабочем каталоге имеются файлы a, b,
c и d. Файлы A, B, C и D отсутствуют, тогда по команде make
мы получим результат - файл Е и файлы A, B, C и D. Рассмот-
рим порядок выполнения Make-программы, когда эти файлы уже
существуют, т.е. при повторном выполнении команды make.
Первый шаг выполнения Make-программы:
E : D C
cat D C > E
Если файлы D и C существуют и не требуется их реконструкция,
а файл E "МОЛОЖЕ", чем файлы D и C, то make прекратит выпол-
нение программы - файл Е готов. Если требуется реконструк-
ция файлов D и/или C, то осуществляется переход к выполнению
подцелей D и/или C, затем возврат к этому правилу. Иначе,
если требуется реконструкция файла E, то выполняется дейст-
вие этого правила и make прекращает выполнение программы -
готов реконструированный файл E. Иначе, если отсутствуют
файлы D и/или C, будут выполнены подцели D и/или C в том
порядке, в котором они указаны в списке зависимостей, затем
выполняется действие этого правила и make прекратит выполне-
ние программы - готов файл E.
Второй шаг выполнения Make-программы:
D : A B
cat A B > D
Если файлы A, B и D существуют и не требуется их реконст-
рукция, то выполняется переход к следующему шагу Make-
программы (файл D уже готов). Если требуется реконструкция
файлов A и/или B, происходит переход к выполнению подцелей A
и/или C, затем возврат к этому правилу. Иначе, если требу-
ется реконструкция файла D, выполняется действие этого пра-
вила и переход к следующему шагу выполнения Make-программы.
Иначе, если отсутствуют файлы A и/или B, будут выполнены
подцели A и/или B в том порядке, в котором они указаны в
- 6 -
списке зависимостей, затем действие этого правила и переход
к выполнению первого правила Make-программы.
Третий шаг выполнения Make-программы:
A : a b
cat a b > A
Проверяется наличие файлов a и b в рабочем каталоге. При
отсутствии хотя бы одного из них выполнение программы прек-
ращается. Затем проверяется наличие файла A, если его нет
или требуется его реконструкция, выполняется действие этого
правила. Иначе осуществляется переход к выполнению второго
правила.
Четвертый шаг выполнения Make-программы:
B : c d
cat c d > B
Действия аналогичны описанным в третьем шаге, переход осу-
ществляется к выполнению второго правила.
Пятый шаг выполнения Make-программ:
C : e f
cat e f > C
Проверяется наличие файлов e и f в рабочем каталоге. При
отсутствии хотя бы одного из них выполнение программы прек-
ращается. Затем проверяется наличие файла C, если его нет
или требуется его реконструкция, выполняется действие этого
правила. Иначе осуществляется переход к выполнению первого
правила.
При вызове интерпретатора make в командной строке можно
указать имя цели. Если, например, необходимо получить файл
D, то командная строка выглядела бы так
% make D
или если необходимо получить файлы C и D, то командная
строка выглядела бы
% make C D
Таким образом, имя файла-цели в командной строке определяет
вход в Make-программу. Если задано несколько входов, то make
выполнит в указанном порядке все необходимые разделы Make-
программы. Если же вход не указан, выполняется первое пра-
вило Make-программы.
- 7 -
В шестом правиле примера цель не является файлом, это
важная особенность make. Программисту предоставляется воз-
можность записать правило, цель и/или подцели которого не
являются файлами. В таком случае цель - это имя входа в
Make-программу (или метка правила). Шестое правило исполь-
зуется в программе для удаления файлов. Следует обратить
внимание на то, что в этом правиле два имени цели (clean и
clear), поэтому в командной строке можно указывать любое имя
входа, например:
% make clean
или
% make clear
В результате выполнения будут удалены файлы A, B, C, D и E.
Все строки действий в правилах передаются на выполнение
shell следующим образом:
sh -c строка_действия
и должны нормально выполняться интерпретатором sh (код возв-
рата 0), иначе (по получении другого кода завершения
командной строки) make прекратит выполнение программы.
Существует способ обойти это условие. Обратите внимание на
действие в 6-м правиле: строка действий начинается с символа
"-", что означает не прекращать работу при неудачном выпол-
нении команды rm.
В Make-программе можно использовать макропеременные.
Механизм макроопределений и подстановок макропеременных в
Make-программе по смыслу аналогичен механизму подстановок в
shell, хотя по синтаксису несколько отличается. Рассмотрим
приведенный выше пример с использованием макропеременных.
Теперь Makefile будет выглядеть так:
- 8 -
SRC1 = a b # макроопределения
SRC2 = c d
SRC3 = e f
SRC4 = A B C D
E : D C
cat D C > E
D : A B
cat A B > D
A : $(SRC1) # A зависит от SRC1
cat $(SRC1) > A
B : $(SRC2) # B зависит от SRC2
cat $(SRC2) > B
C : $(SRC3) # C зависит от SRC3
cat $(SRC3) > C
clean clear:
-rm -f $(SRC4)
Первые строки Make-программы - строки с макроопределениями,
где каждой переменной SRC присваиваются значения. В правилах
выполняется операция подстановки значения макропеременной,
например $(SRC1). Макропеременные позволяют манипулировать
списками имен файлов при минимальных изменениях в тексте
Make-программы.
Интерпретатор make реализует механизм обработки умолча-
ний зависимостей файлов со стандартными суффиксами имен.
Например, файл с суффиксом имени .o можно получить из файлов
с суффиксами имен .c (язык программирования Си) и .s (язык
ассемблер). Рассмотрим пример. Допустим, имеются файлы a.c,
b.c, c.c и d.s, образующие программу, которую назовем pro-
gram. Файлы a.c, b.c и c.c содержат строку
# include program.h
т.е. зависят от файла program.h. Make-программа для работы с
этими файлами будет содержать 3 строки
program: a.o b.o c.o d.o
cc a.o b.o c.o d.o -o program
a.o b.o c.o: program.h
По команде make будет создан файл program. При первом
выполнении получим на экране дисплея:
- 9 -
cc -c a.c
cc -c b.c
cc -c c.c
as - -o d.o d.s
cc a.o b.o c.o d.o -o program
Обратите внимание на то, что интерпретатор определил необхо-
димые действия над исходными файлами с суффиксами имен .c и
.s, хотя имена этих файлов в Make-программе не указаны, и
правильно осуществил сборку программы. Теперь, допустим, мы
вызываем make на выполнениe второй раз после редактирования
файла program.h, при этом получим:
cc -c a.c
cc -c b.c
cc -c c.c
cc a.o b.o c.o d.o -o program
Если выполнить Make-программу после редактирования файла
b.c, то получим:
cc -c b.c
cc a.o b.o c.o d.o -o program
Наконец, если, допустим, необходимо получить файл c.o, то
можно выполнить команду
make c.o
Механизм умолчаний и обработки суффиксов спроектирован для
автоматизации программирования Make-программ; он существенно
сокращает размеры программ и количество ошибок в них.
3. Соглашения языка Make
Ниже в компактной форме приводятся основные синтакси-
ческие конструкции языка Make. В следующих параграфах по
каждой конструкции будут даны описания и примеры.
идентификатор
последовательность букв, цифр и символа "_", содержащая
шаблоны имен файлов ([...], ?, *), символы / и "."
идентификатор = значение
определение макропеременной. В правой части могут быть
макроподстановки, а также вся правая часть может быть
пустой строкой.
$(идентификатор)
${идентификатор}
$символ
- 10 -
подстановка значения макропеременной. Специальное зна-
чение символа $ можно отменить, указывая $$.
комментарий
текст, следующий за символом # и до конца строки. Спе-
циальное значение символа # отменяется, если он указан
в кавычках.
обратная наклонная черта
символ продолжения строки. Специальное значение сим-
вола \ отменяется, если он указан дважды.
пустая строка, пробелы
пробелы служат разделителями слов в командной строке,
лишние пробелы и табуляции игнорируются. Пустая строка
всюду игнорируется.
список_зависимостей
правило в общем виде. Список_действий может быть пус-
тым.
.первый_суффикс.второй_суффикс:
правило с указанием зависимостей суффиксов. Если в
Make-программе содержится хотя бы одно правило с суф-
фиксами, отличными от предопределенных, в нее необхо-
димо включить правило:
.SUFFIXES: список_суффиксов
список_целей :[:] список_подцелей
список_зависимостей. Список_подцелей может быть пус-
тым. Правила с одним и двумя символами двоеточия в
списке_зависимостей отличаются порядком выполнения
списка_подцелей и списка_действий.
имя_цели [имя_цели]
список_целей. Имя_цели - идентификатор. Можно исполь-
зовать символ / и точку. Имя_цели может быть именем
файла или каталога, тогда включается выполнение меха-
низма реконструкции. Имя_цели может не быть именем
файла, тогда механизм реконструкции не включается, а
имя_цели является меткой (именем правила, входа в
Make-программу).
имя_подцели [имя_подцели] [#комментарий]
список_подцелей. Имя_подцели - идентификатор. Можно
- 11 -
использовать шаблоны имен файлов "*", "?", [...], сим-
вол / и точку. Имя_подцели может быть именем файла,
для которого записано или не записано правило в Make-
программе, в этих случаях включается механизм реконст-
рукции. Имя_подцели может не быть именем файла, в этом
случае механизм реконструкции не включается, а имя_цели
является меткой (именем правила, входа в Make-
программу).
строка_действия [#комментарий]
...............
строка_действия [#комментарий]
список_действий. Любые командные строки ДЕМОС и управ-
ляющие конструкции Shell.
'табуляция'командная_строка_shell
строка_действия.
Строка_действия может быть указана в строке зависимос-
тей через символ ";". Строку_действия можно указывать в
следующих форматах:
'табуляция'командная__строка__shell 1
'табуляция'@командная_строка__shell 2
'табуляция'-командная_строка__shell 3
'табуляция'-@командная_строка_shell 4
В первом формате командная строка выводится на печать;
если код возврата после ее выполнения не 0, make прек-
ращает выполнение программы по ошибке. Во втором фор-
мате командная строка не выводится на печать; если код
возврата после ее выполнения не 0, make прекращает
выполнение программы по ошибке. В третьем формате
командная строка выводится на печать; если код возврата
после ее выполнения не 0, make игнорирует ошибку и
выполнение программы продолжается. В четвертом формате
командная строка не выводится на печать; если код возв-
рата после ее выполнения не 0, make игнорирует ошибку
и выполнение программы продолжается. Простая командная
строка (одна команда ДЕМОС с аргументами) выполняется
без порождения оболочки. Другие командные строки
выполняются sh следующим образом: sh -c
командная_строка_shell
Для сокращения обозначений предусмотрены макроперемен-
ные с предопределенными именами. В правиле без суффиксов
- 12 -
строка_действия может включать следующие предопределенные
макропеременные:
@ имя цели;
? имена файлов из списка подцелей, которые МОЛОЖЕ
файла_цели. Эти файлы участвуют в реконструкции цели.
В правиле с суффиксами строка_действия может включать
следующие предопределенные макропеременные:
* основа_имени_цели;
@ основа_имени_цели.второй_суффикс;
<< основа_имени_цели.первый_суффикс.
4. Использование макропеременных
При выполнении Make-программы значения макропеременных
устанавливаются в строках макроопределений и/или в командной
строке при запуске make на исполнение. Кроме того, сущест-
вуют макропеременные с предопределенными именами, значения
которых устанавливаются при выполнении Make-программы. К ним
относятся: макропеременная @, ее значение - имя_цели; макро-
переменная ?, ее значение - имена тех файлов_подцелей, кото-
рые МОЛОЖЕ файла_цели.
Предопределенные макропеременные "@" и "?" используются
только в списке действий правила и в каждом правиле имеют
свои значения. Определять значения макропеременных можно в
любом месте Make-программы, но не внутри правила. Интерпре-
татор make составляет список имен макропеременных и присваи-
вает им значения в процессе чтения Make-программы. Если зна-
чение макропеременной переопределяется, то ей присваивается
новое значение; если используются макроопределения с вложен-
ными макроподстановками, то значения устанавливаются с уче-
том всех присвоений в Make-программе. Если значение макро-
переменной задается в командной строке при запуске Make-
программы на исполнение, то все определения этой макропере-
менной в Make-программе игнорируются и используется значе-
ние, взятое из командной строки. Состояние ошибки порожда-
ется, если используется рекурсия при присвоении значения
макропеременным. Например,
A = $B
B = $A
приведет к аварийному завершению выполнения Make-программы.
В макроопределениях можно использовать метасимволы шаб-
лонов имен файлов shell. Допустим, рабочий каталог имеет
- 13 -
вид:
-rw-r--r-- 1 user 15658 Авг 6 16:03 1.m
-rw-r--r-- 1 user 2158 Авг 8 16:38 2.m
-rw-r--r-- 1 user 5185 Авг 9 17:38 3.m
-rw-r--r-- 1 user 4068 Июл 28 20:56 6.m
-rw-r--r-- 1 user 100 Авг 9 14:30 f2
-rw-r--r-- 1 user 66 Авг 9 17:42 f3
Пример Make-программы в Makefile :
A = *
B = f?
C = [0-9].*
aaa :
echo $A
echo $B
echo $C
После выполнения команды make -s получим на экране дисплея:
1.m 2.m 3.m 6.m f2 f3
f2 f3
1.m 2.m 3.m 6.m
В Make-программе часто бывает необходимо манипулировать име-
нами каталогов и файлов. Механизм макроподстановок предла-
гает удобные средства для этого. Пример:
A = *
B = [pg]*
C = f?r*
DIR1 = .
DIR2 = /etc
DIR3 = /usr/bin
aaa :
echo ${DIR1}/$A
echo ${DIR2}/$B
echo ${DIR3}/$C
После выполнения получим:
./1.m ./2.m ./3.m ./6.m ./f2 ./f3
/etc/getty /etc/group /etc/passwd
/usr/bin/fgrep
Рассмотрим пример, в котором демонстрируются всевозмож-
ные способы использования макропеременных. Допустим, имеется
Makefile
- 14 -
БИБЛИОТЕКА = ПОЛКА ${ДРУГОЕ}
ДРУГОЕ = Документы
Шкаф = ПОЛКА
папка = справки, копии.
СПРАВОЧНИКИ =
ЖУРНАЛЫ =
Словари = толковые, иностранных языков.
ТЕХНИЧЕСКАЯ = $(СПРАВОЧНИКИ) $(Словари)
ХУДОЖЕСТВЕННАЯ = проза, поэзия, драматургия.
t = Справка с места жительства.
x = Копия свидетельства о рождении.
файлы = d e
библиотека : ${БИБЛИОТЕКА}
echo 'Действия правила' $@
echo '$$? - список подцелей :' $?
echo '$$@ - имя цели :' $@
echo 'Техническая : ' $(ТЕХНИЧЕСКАЯ)
echo 'Худ. : ' $(ХУДОЖЕСТВЕННАЯ)
echo ' '
${Шкаф} : b c
echo 'Действия правила' $@
echo '$$? - список подцелей :' $?
echo '$$@ - имя цели :' $@
echo ' '
${ДРУГОЕ}: ${файлы}
echo 'Действия правила' $@
echo '$$? - список подцелей :' $?
echo '$$@ - имя цели :' $@
echo 'Папка : ' ${папка}
echo $t
echo $x
echo ' '
b:
c:
d:
e:
Следует обратить внимание на то, что $буква используется для
подстановки значения макропеременной, имя которой состоит из
одной буквы, а $(идентификатор) и ${идентификатор} равно-
ценны. Правила b, c, d и e включены исключительно для
демонстрации значений макропеременной "?". Теперь, если
выполнить команду make -s, получим на экране дисплея:
- 15 -
Действия правила ПОЛКА
$? - список подцелей : b c
$@ - имя цели : ПОЛКА
Действия правила Документы
$? - список подцелей : d e
$@ - имя цели : Документы
Папка : справки, копии.
Справка с места жительства.
Копия свидетельства о рождении.
Действия правила библиотека
$? - список подцелей : ПОЛКА Документы
$@ - имя цели : библиотека
Техническая :толковые, иностранных языков.
Худ. : проза, поэзия, драматургия.
В командной строке можно присвоить значение макропеременной.
После команды
make -s Словари = английский, немецкий.
получим на экране дисплея:
Действия правила ПОЛКА
$? - список подцелей : b c
$@ - имя цели : ПОЛКА
Действия правила Документы
$? - список подцелей : d e
$@ - имя цели : Документы
Папка : справки, копии.
Справка с места жительства.
Копия свидетельства о рождении.
Действия правила библиотека
$? - список подцелей : ПОЛКА Документы
$@ - имя цели : библиотека
Техническая : английский, немецкий.
Худ. : проза, поэзия, драматургия.
5. Выполнение правил в Make-программе
Существует несколько разновидностей правил:
с одним и двумя двоеточиями;
с одинаковыми именами целей;
не содержащие списка действий;
- 16 -
не содержащие списка подцелей и/или списка действий;
правила, предопределенные в интерпретаторе make, кото-
рые программист может включать в Make-программу.
Кроме того, имеются некоторые различия в выполнении
Make-программы, когда имя цели не является файлом. В общем
случае правило может содержать одно или два двоеточия в
качестве разделителей списков целей и подцелей. Порядок
выполнения в этих правилах одинаков, если в них указаны раз-
личные имена целей.
Правило выполняется следующим образом: сначала выполня-
ются правила с именами целей из списка подцелей, затем спи-
сок действий. Если в списке подцелей указано имя файла, для
которого правило не определено, то время создания (модифика-
ции) файла используется для определения необходимости
реконструкции цели. Если имя подцели не является файлом,
оно должно быть меткой правила, иначе порождается состояние
ошибки. Допустим, в Make-программе записано правило
monitor.c : monitor.h
Это означает, что файл monitor.c зависит от файла monitor.h.
Если monitor.h действительно имеется в рабочем каталоге, то
любые изменения в нем приведут к реконструкции файла
monitor.o, если файл monitor.h отстутствует, make прекращает
выполнять программу по ошибке. В Make-программе могут
встречаться случаи, когда необходимо для одной цели записать
несколько правил, тогда существенно важно, сколько двоеточий
указано в правиле. Ниже приведены схемы, в которых цифрами
показан порядок выполнения этих правил.
Порядок выполнения нескольких правил с одинаковым
именем_цели и одним двоеточием: сначала выполняются правила,
связанные со списками подцелей, затем список_действий одного
из правил. Список действий разрешается указывать только в
одном из таких правил:
имя_цели_А : список_подцелей <---| 1 |
имя_цели_А : список_подцелей <---| 2 |
список_действий <---| 4 |
имя_цели_А : список_подцелей <---| 3 |
Порядок выполнения нескольких правил с одинаковым
именем_цели и двумя двоеточиями. Список действий может быть
в каждом правиле:
- 17 -
имя_цели_А :: список_подцелей <---| 1 |
список_действий <---| 2 |
имя_цели_А :: список_подцелей <---| 3 |
список_действий <---| 4 |
имя_цели_А :: список_подцелей <---| 5 |
список_действий <---| 6 |
6. Режимы выполнения Make-программы
Интерпретатор make предоставляет ряд возможностей для
управления выполнением Make-программы. Для этой цели исполь-
зуются следующие директивы:
.SILENT - не печатать строки действий;
.IGNORE - игнорировать ошибки действий;
.DEFAULT - выполнить альтернативное действие;
.PRECIOUS - удалить недостроенный файл.
Директивы .SILENT и .IGNORE можно указывать как в Make-
программе, так и в командной строке ключами -s и -i, DEFAULT
и PRECIOUS только в Make-программе. Допустим имеется следу-
ющая Make-программа:
aa: bb
echo Действия правила $@
bb:
echo Действия правила $@
ppp # Несуществующая команда
sort e # Нет файла e
После выполнения получим сообщения:
echo Действия правила bb
Действия правила bb
ppp # Несуществующая команда
sh: ppp: не найден
*** Код ошибки 1
Конец.
Интерпретатор make прекратил работу по первой ошибке. Кроме
того, на экран дисплея выводятся строки действий. Отменить
вывод строк действий можно следующими способами:
указать ключ -s в командной строке при запуске make;
указать в Make-программе директиву SILENT;
- 18 -
начинать каждую строку действий символом @.
В первых двух случаях результат одинаков - не будут
выводиться строки действий. В третьем случае не будут выво-
диться строки действий вида
'табуляция'@строка_действий
Пример использования директивы SILENT.
aa: bb
echo Действия правила $@
bb:
echo Действия правила $@
ppp # Несуществующая команда
sort e # Нет файла e
.SILENT:
После выполнения программы получим:
Действия правила bb
sh: ppp: не найден
*** Код ошибки 1
Конец.
По любой ошибке make прекратит выполнение программы. Сущест-
вуют следующие способы игнорирования ошибок (Make-программа
продолжает выполняться):
указать ключ -i при запуске make на выполнение;
указать в Make-программе директиву IGNORE;
после символа табуляция указать символ "-".
В первом и втором случаях будут проигнорированы все
ошибки при выполнении строк действий, в третьем игнорируются
ошибки в тех строках действия, которые указаны следующим
образом:
'табуляция'-строка_действий
Пример использования директив SILENT и IGNORE. Обработка
ошибок при выполнении действий в правилах
- 19 -
aa: bb
echo Действия правила $@
bb:
echo Действия правила $@
ppp # Несуществующая команда
sort e # Нет файла e
.SILENT:
.IGNORE:
После выполнения программы получим:
Действия правила bb
sh: ppp: не найден
*** Код ошибки 1 (игнорирован)
sort: не могу открыть e
*** Код ошибки 1 (игнорирован)
Действия правила aa
Правило DEFAULT используется для указания альтернативных
действий по отсутствующему в данный момент файлу. Правило
DEFAULT позволяет записать список действий, которые будут
выполняться для всех отсутствующих файлов, поэтому требуется
определенная осторожность, например:
- 20 -
aa: bb
echo Действия правила $@
bb: a b c d
echo Действия правила $@
ppp # Несуществующая команда
sort e # Нет файла e
.SILENT:
.IGNORE:
.DEFAULT:
echo Действия правила .DEFAULT для $@
После выполнения программы получим:
Действия правила .DEFAULT для a
Действия правила .DEFAULT для b
Действия правила .DEFAULT для c
Действия правила .DEFAULT для d
Действия правила bb
sh: ppp: не найден
*** Код ошибки 1 (игнорирован)
sort: не могу открыть e
*** Код ошибки 1 (игнорирован)
Действия правила aa
Часто бывает необходимо прекратить выполнение Make-
программы. Это не приведет к фатальным последствиям, так
как сохраняется структура динамических (зависящих от вре-
мени) связей файлов. Однако, если создание некоторого файла
не завершилось и он тем не менее образовался, его желательно
удалить перед повторным запуском интерпретатора. Это можно
сделать автоматически, используя директиву PRECIOUS, напри-
мер:
aaa: file
sort file > $@
.PRECIOUS:
Если в момент синхронного исполнения Make-программы ввести
сигнал CTRL/C, файл $@ будет удален.
7. Правила с суффиксами
В Make-программе можно записать одно правило для обра-
ботки различных файлов. В этом случае это одно правило мно-
гократно выполняется для различных файлов, что существенно
сокращает размеры Make-программ, упрощает их разработку. Все
полезные свойства make при этом сохраняются. Механизм
- 21 -
выполнения таких правил строится на суффиксах имен файлов.
Допустим, из файла с именем основа.суффикс_1 необходимо
получить файл с именем основа.суффикс_2, тогда обычное пра-
вило будет выглядеть так:
основа.суффикс_2 : основа.суффикс_1
список действий
Понятно, что для группы файлов, основы имен которых раз-
личны, а первый и второй суффиксы имен одинаковы, желательно
было бы записать одно правило обработки. Например, файлы *.c
обычно преобразуются в файлы *.o одним списком действий, и,
следовательно, эти правила желательно записывать в виде
одного правила. Интерпретатор make предлагает эту возмож-
ность в правилах вида
.суффикс_1.суффикс_2:
список_действий
.SUFFIXES: .суффикс_1 .суффикс_2
Если в Make-программе записаны эти правила, интерпретатор,
получив в качестве аргумента имя файла с именем
основа.суффикс_1, выполнит указанное выше правило, и мы
получим результат - файл с именем основа.суффикс_2. Поря-
док выполнения правила с суффиксами такой же, как и в обыч-
ном правиле. Предопределенное правило SUFFIXES используется
для указания списка суффиксов, который может содержать более
двух суффиксов. Порядок суффиксов в списке роли не играет.
Естественно, для каждой пары суффиксов в Make-программе
должны быть записаны соответствующие правила. В правилах с
суффиксами используются предопределенные макропеременные:
@ - имя_результатa (основа.суффикс_2);
<< - имя_аргументa (основа.суффикс_1);
* - основа.
Рассмотрим пример. Допустим, имеются исходные файлы *.c
и *.k. Необходимо из них получить файлы *.t, а из них -
файл result. Допустим также, что файлы *.c зависят от файла
file.h (содержат строку #include file.h). Граф преобразова-
ний файлов в этом случае выглядит так:
- 22 -
result
|
.t
|
--------
| |
.c .k
Программа, реализующая этот граф, может быть следующей:
- 23 -
result: d.t a.t b.t c.t
echo 'Действия правила' $@
echo ' Значение $$@ - ' $@
echo ' Значение $$? - ' $?
touch $@
.k.t :
echo 'Действия правила .k.t :'
echo ' Значение $$* - ' $*
echo ' Значение $$@ - ' $@
echo ' Значение $$? - ' $?
echo ' Значение $$< - ' $<
touch $@
.c.t :
echo 'Действия правила .c.t :'
echo ' Значение $$* - ' $*
echo ' Значение $$@ - ' $@
echo ' Значение $$? - ' $?
echo ' Значение $$< - ' $<
touch $@
a.c b.c c.c : file.h
.SUFFIXES: .t .c .k
После выполнения команды make -rs получим:
Действия правила .k.t :
Значение $* - d
Значение $@ - d.t
Значение $? - d.k
Значение $< - d.k
Действия правила .c.t :
Значение $* - a
Значение $@ - a.t
Значение $? - file.h a.c
Значение $< - a.c
Действия правила .c.t :
Значение $* - b
Значение $@ - b.t
Значение $? - file.h b.c
Значение $< - b.c
Действия правила .c.t :
Значение $* - c
Значение $@ - c.t
Значение $? - file.h c.c
Значение $< - c.c
Действия правила result
- 24 -
Значение $@ - result
Значение $? - d.t a.t b.t c.t
Заметим, что правило .k.t выполняется только один раз, пра-
вило .c.t - три раза, как и требовалось для списка исходных
файлов, указанных в списке подцелей правила result. Смысл
макропеременной "?" в правиле с суффиксом тот же, что и в
обычном файле - список подцелей. Макропеременная "@" в обыч-
ном правиле - имя цели, а здесь имя результата - файл с име-
нем основа.суффикс_2.
Интерпретатор make содержит список правил для стандарт-
ных суффиксов имен файлов и определения самих суффиксов.
Этот список подключается к Make-программе пользователя, если
make запускается на выполнение без ключа -r. Программист
полностью освобождается от указания правил преобразований
файлов со стандартными суффиксами имен. Обрабатываются
файлы, имена которых включают следующие суффиксы:
.out - файл в загрузочном формате;
.o - объектный файл;
.c - файл на языке Си;
.f - файл на языке Фортран;
.p - файл на языке Паскаль;
.s - файл на ассемблере;
.l - файл на lex;
.y - файл на yacc;
Кроме того, в Make-программу включаются предопределенные
макропеременные:
LOADLIBES = # имена библиотек
AS = as - # имя ассемблера
CC = cc # имя Си-компилятора
CFLAGS = # ключи Си-компилятора
PC = pc # имя Паскаль-компилятора
PFLAGS = # ключи Паскаль-компилятора
FF = f77 # имя f77-компилятора
FFLAGS = # ключи f77-компилятора
LEX = lex # имя генератора
LFLAGS = # ключи lex
YACC = yacc # имя генератора
YFLAGS = # ключи yacc
Значения предопределенных макропеременных можно менять в
Make-программе, например, если ввести строку CFLAGS = -O,
то предопределенная макропеременная CFLAGS будет иметь новое
значение в Make-программe. Ниже приводятся граф зависимос-
тей целей и список правил Make-программы, реализующей обра-
ботку файлов по суффиксам имен.
- 25 -
.c .f .p .s .l .y
| | | | | |
|-------------------------
|
|
---> .out <--- .o ---
|
|
--------------------------
| | | | | |
.p .f .c .s .l .y
|
---
| |
.l .y
- 26 -
.l.out:
$(LEX) $<
$(CC) $(CFLAGS) lex.yy.c $(LOADLIBES) -ll -o $@
rm lex.yy.c
.y.out:
$(YACC) $(YFLAGS) $<
$(CC) $(CFLAGS) y.tab.c $(LOADLIBES) -ly -o $@
rm y.tab.c
.f.out:
$(FF) $(FFLAGS) $< $(LOADLIBES) -o $@
-rm $*.o
.o.out:
$(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
.c.out:
$(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
.p.out:
$(PC) $(PFLAGS) $< $(LOADLIBES) -o $@
.s.out:
$(CC) $(CFLAGS) $< $(LOADLIBES) -o $@
.l.c:
$(LEX) $<
mv lex.yy.c $@
.y.c:
$(YACC) $(YFLAGS) $<
mv y.tab.c $@
.l.o:
$(LEX) $(LFLAGS) $<
$(CC) $(CFLAGS) -c lex.yy.c
rm lex.yy.c; mv lex.yy.o $@
.y.o:
$(YACC) $(YFLAGS) $<
$(CC) $(CFLAGS) -c y.tab.c
rm y.tab.c; mv y.tab.o $@
.s.o:
$(AS) -o $@ $<
.f.o:
$(FF) $(FFLAGS) -c $<
.c.o:
$(CC) $(CFLAGS) -c $<
- 27 -
.p.o:
$(PC) $(PFLAGS) -c $<
.SUFFIXES: .out .o .c .f .p .y .l .s
Допустим, имеются файлы f[1-6].c с исходными текстами прог-
раммы result. Файлы f[1-3].c содержат строки
# include file1.h
# include file2.h
и файлы f[4-6].c содержат строку
# include file3.h
Следующая программа управляет созданием программы result:
CFLAGS = -O
LDFLAGS = -s -n -o
OBJS = f1.o f2.o f3.o f4.o f5.o f6.o
result: ${OBJS}
${CC} ${OBJS} ${LDFLAGS} $@
f1.o f2.o f3.o : file1.h file2.h
f4.o f5.o f6.o : file3.h
Так выглядит протокол выполнения:
cc -O -c f1.c
cc -O -c f2.c
cc -O -c f3.c
cc -O -c f4.c
cc -O -c f5.c
cc -O -c f6.c
cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Теперь изменим время модификации файла командой
touch file3.h
и снова выполним программу, в результате получим:
cc -O -c f4.c
cc -O -c f5.c
cc -O -c f6.c
cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Теперь изменим время модификации файла командой touch f3.c и
снова выполним программу
- 28 -
cc -O -c f3.c
cc f1.o f2.o f3.o f4.o f5.o f6.o -s -n -o result
Программист может отключить стандартные определения и список
правил с суффиксами (ключ -r), может их использовать наряду
с теми, что определены в Make-программе. Допустим, к имею-
щимся файлам предыдущего примера добавляются файлы a1.t,
a2.t и a3.t, их необходимо обработать программой sed, выхо-
дом которой будут файлы a1.c a2.c a3.c. Теперь Make-
программа выглядит так:
CFLAGS = -O
LDFLAGS = -o
OBJS = a1.o a2.o a3.o f1.o f2.o f3.o f4.o f5.o f6.o
result: ${OBJS}
${CC} ${OBJS} ${LDFLAGS} $@
f1.o f2.o f3.o : file1.h file2.h
f4.o f5.o f6.o : file3.h
a1.c: a1.t
a2.c: a2.t
a3.c: a3.t
.t.c:
sed s/aaa/bbb/ < $< > $*.c
.SUFFIXES: .t
Протокол выполнения программы:
sed s/aaa/bbb/ < a1.t > a1.c
cc -O -c a1.c
sed s/aaa/bbb/ < a2.t > a2.c
cc -O -c a2.c
sed s/aaa/bbb/ < a3.t > a3.c
cc -O -c a3.c
cc -O -c f1.c
cc -O -c f2.c
cc -O -c f3.c
cc -O -c f4.c
cc -O -c f5.c
cc -O -c f6.c
cc a1.o a2.o a3.o f1.o f2.o f3.o f4.o f5.o f6.o -o result
Make допускает обработку суффиксов только одного уровня вло-
женности. В правиле SUFFIXES указан только суффикс t, суф-
фикс c определен в подключаемом списке суффиксов.
- 29 -
8. Управление архивом в Make-программе
В ДЕМОС существуют два типа архивов: архив текстовых
файлов и архив объектных файлов (имеющий структуру библио-
теки объектных модулей). Созданный архив является одним
файлом, а файлы, из которых он собран, образуют его части.
Управление архивом включает две задачи: создание
файла_архива определенной структуры и его модификация
(добавление, удаление и замена частей, изменение структуры,
декомпозиция архива на файлы, из которых он был образован).
Для решения таких задач Make-программа должна обеспечивать
работу с целями двух типов: файл_архив и файл для включения
в архив. При этом оценивается время последней модификации
включаемого в архив файла (а не время создания архива или
записи файла_части в архив). Имя файла_архива может указы-
ваться в списке зависимостей правил как обычная цель:
имя_архивного_файла(имя_файла_для_включения_в_архив)
Кроме имен файлов, при работе с библиотекой объектных моду-
лей можно указывать имена функций
имя_файла_библиотеки((_внешнее_имя_библ_функции))
Рассмотрим фрагмент Make-программы для построения библиотеки
с именем libP.a:
L = libP.a
CFLAGS = -O
$(L)::
ar r $(L)
$(L):: $(L)(Ia.o) $(L)(La.o) $(L)(Da.o)
# Iabs, Labs, Dabs - имена функций
$(L)(Iabs.o): lib/Ia.c
$(CC) $(CFLAGS) lib/Ia.c
ar r $(L) Ia.o
-rm -f Ia.o
$(L)(Labs.o): lib/La.c
$(CC) $(CFLAGS) lib/La.c
ar r $(L) La.o
-rm -f La.o
$(L)(Dabs.o): lib/Da.c
$(CC) $(CFLAGS) lib/Da.c
ar r $(L) Da.o
-rm -f Da.o
У такого способа работы с библиотекой есть недостаток -
- 30 -
Make-программа должна содержать структуру библиотеки. Список
имен библиотечных модулей во втором правиле "$(L)::" соот-
ветствует порядку их размещения в библиотеке. Еще одно неу-
добство заключается в том, что если бы библиотека содержала
большое количество модулей, например 100, то потребовалась
бы запись 100 правил в программе. Для построения однопро-
ходных библиотек такой способ указания структуры имеет
существенный недостаток, так как изменение исходного текста
объектного модуля может привести к необходимости изменить
структуру библиотеки, а это, в свою очередь, приведет к
необходимости реконструировать Make-программу. Кроме того,
при построении библиотеки необходимо, чтобы все объектные
файлы были в рабочем каталоге.
Рассмотрим Make-программу, в которой эти недостатки
устранены. Возьмем в качестве примера файлы с исходными
текстами модулей объектной библиотеки mylib
m1.c m2.c m3.c m4.s m5.c m6.c m7.s
Допустим, структура однопроходной библиотеки с учетом вызо-
вов модулей должна быть такой:
m4.o m2.o m1.o m5.o m6.o m7.o m3.o
Текст Make-программы:
- 31 -
CFLAGS = -O
SRC = m1.c m2.c m3.c m4.s m5.c m6.c m7.s
LIB = mylib
${LIB}: ${SRC}
echo $? | sed s/\\.[cs]/\\.o/g > list
make `cat list`
ar cr $@ `cat list`
lorder $@ | tsort > list
-@if cmp list topology ; \
then \
rm -f `cat list` list;\
else \
ar x ${LIB}; rm $@;\
mv list topology;\
ar cr $@ `cat topology`;\
rm -f `cat topology`;\
echo Структура $@ изменилась.;\
fi
ranlib ${LIB}
echo Библиотека $@ готова.
m1.c : x.h
touch m1.c
m2.c : x.h y.h
touch m2.c
m3.c : x.h
touch m3.c
m5.c : y.h
touch m5.c
m6.c : x.h
touch m6.c
Рассмотрим простые случаи реконструкции уже существующей
библиотеки. Допустим, изменился исходный текст одного из
модулей. В этом случае достаточно на его место в библиотеке
включить новую версию объeктного файла этого модуля. Чтобы
не компилировать другие файлы библиотеки, можно использовать
предопределенную макропеременную "?" - список файлов, кото-
рые стали МОЛОЖЕ файла mylib. Как уже говорилось выше, в
каталоге нет объектных файлов. Следовательно, в Make-
программу необходимо включить предопределенные средства
обработки суффиксов, а в качестве аргумента формировать имя
цели с суффиксом o, тогда make автоматически построит новый
объектный файл этого модуля. В примере файл list использу-
ется в качестве временного рабочего файла. Строка вида
echo $? | sed s/\\.[cs]/\\.o/g > list
- 32 -
записывает в файл list список целей с суффиксом o. Редактор
sed используется в этой командной строке для замены суффик-
сов c и s на o. Таким образом, файл list содержит имена
файлов-целей, которые необходимо создать и на этой основе
реконструировать библиотеку. Это можно сделать, например,
так:
make `cat list`
В результате выполнения будут созданы нужные объектные
файлы. После этого можно включить объектные модули на свои
места в библиотеке mylib
ar rc mylib `cat list`
Если же библиотека mylib отсутствует,то она создается, если
модуль в библиотеке исходно отсутствовал, он записывается в
конец библиотеки (так работает команда ar с ключами cr).
По команде "make `cat list`" выполняться будет Makefile
(здесь срабатывает механизм умолчания имени файла с Make-
программой). Таким образом, имеет место рекурсивный вызов
Make-программы. При рекурсивном вызове в качестве имен
целей передаются имена объектных файлов, которые необходимо
получить и включить в библиотеку. В интерпретаторе make нет
средств, позволяющих менять список целей в процессе выполне-
ния Make-программы. Когда это необходимо, список целей соз-
дается в Make-программе и передается на выполнение как спи-
сок аргументов при вызове подпрограммы. Рекурсия в примере
понадобилась для того, чтобы не записывать полный список
правил для всех файлов-целей, а вызвать make с актуальным
списком аргументов.
Возможна такая реконструкция библиотеки, когда меняется
ее структура. Для этого в Make-программе используются
команды lorder и tsort. Lorder выводит список вызовов моду-
лей существующей библиотеки, а tsort сортирует этот список
таким образом, чтобы структура библиотеки была непротиворе-
чивой, т.е. однопроходный редактор связей мог бы за одно
чтение библиотечного файла найти все необходимые модули. В
Make-программу включаются действия, в которых строится
структура библиотеки во временном файле list и запоминается
в сохраняемом файле topology. Возможны следующие случаи
реконструкции библиотеки:
Реконструкция библиотеки не изменила ее структуры, в
этом случае файл topology не отличается от файла list.
Реконструкция изменила структуру библиотеки. В этом
случае файл topology отличается от файла list и требуется,
во-первых, получить верную структуру и запомнить ее в файле
topology, во-вторых, извлечь из библиотеки все объектные
модули (разобрать библиотеку и удалить файл с библиотекой),
- 33 -
затем собрать ее в соответствии с новой структурой. Разоб-
рать библиотеку можно командой
ar x mylib
В рабочем каталоге будут созданы копии объектных модулей из
библиотечного файла. Библиотечный файл при этом сохраняется.
Разобранную библиотеку можно собрать заново, используя
информацию о структуре библиотеки в файле topology и
команду:
ar rc mylib `cat topology`
В shell допускается записать if одной строкой следующим
образом:
if список_команд;\
then \
список_команд;\
else \
список_команд;\
fi
В Makefile эта конструкция записана:
-@if cmp list topology ; \
then \
rm -f `cat list` list;\
else \
ar x $(LIB); rm $@;\
mv list topology;\
ar cr $@ `cat topology`;\
rm -f `cat topology`;\
echo Структура $@ изменилась.;\
fi
Первая строка в ней выглядит так:
'табуляция'-@if cmp list topology ; \
Остальные строки имеют более 8 ведущих пробелов. Символ "-"
указан, так как в отдельных версиях shell оператор if при
нормальном завершении возвращает не 0. Символ "@" отменяет
вывод этой строки перед выполнением.
Приведенная Make-программа позволяет работать с любыми
объектными библиотеками. Для конкретной библиотеки (или
архива) нужно изменить макропеременные LIB, SRC и записать
зависимости от файлов включений. Если необходимо работать с
текстовыми архивами, то достаточно удалить строку ranlib
$@. Рассмотрим на примерах работу программы при различных
исходных условиях.
- 34 -
Библиотеки нет, структура неизвестна
make -s
cc -c m1.c
cc -c m2.c
cc -c m3.c
as - -o m4.o m4.s
cc -c m5.c
cc -c m6.c
as - -o m7.o m7.s
cmp: не могу открыть topology
Структура mylib изменилась.
Библиотека mylib готова.
Библиотека mylib имеется, структура остается без изме-
нений, модифицируется файл x.h
touch x.h
make -s
cc -c m1.c
cc -c m2.c
cc -c m3.c
cc -c m6.c
Библиотека mylib готова.
Меняется содержимое библиотечного модуля m5.c. Меня-
ется структура библиотеки: модуль m5 вызывает теперь модуль
m1
make -s
cc -c m5.c
Структура mylib изменилась.
Библиотека mylib готова.
В файле topology теперь новая структура библиотеки
m4.o m2.o m5.o m1.o m6.o m7.o m3.o
Добавляется новый модуль в библиотеку. Придется изме-
нить строку SRC в Make-программе. Имя модуля m8.c , вызы-
вает он модуль m5.c
cc -c m8.c
list topology различны: char 12, line 3
Структура mylib изменилась.
Библиотека mylib готова.
В файле topology теперь новая структура библиотеки
m4.o m2.o m8.o m5.o m1.o m6.o m7.o m3.o
- 35 -
Изменим модуль m1.c так, чтобы он вызывал модуль m2.c,
а модуль m2.c вызывал m1.c, т.е. получается зацикленная вза-
имная зависимость библиотечных модулей m1 и m2
make -s
cc -c m1.c
tsort: зацикленная зависимость
tsort: m2.o
tsort: m1.o
Структура mylib изменилась.
Библиотека mylib готова.
Команда tsort вывела сообщение об ошибке в структуре библио-
теки. Библиотека собрана, но пользоваться ею нельзя, необ-
ходимо исправить структуру модуля. Удалять файл mylib не
нужно, так как он содержит все объектные модули, которые
понадобятся для новой сборки.
9. Особенности программирования на языке Make
Всюду в примерах Make-программа размещалась в одном
Makefile. Существует возможность разместить ее в файле с
другим именем и при вызове интерпретатора make указать это
имя
make -f имя_файла
Иногда возникает необходимость использовать несколько Make-
файлов, образующих одну Make-программу, тогда при вызове
make можно указать
make -f имя_файла1 -f имя_файла2 и т.д.
Указанные файлы составят текст одной Make-программы в том
порядке, в котором они указаны в командной строке.
Внутри одной Make-программы можно вызывать make на
выполнение другой Make-программы, например:
LLL: a b c d
make -k -f имя_Make-файла $?
В том случае, если эта командная строка не может быть нор-
мально выполнена, ключ -k указывает на необходимость продол-
жить выполнение других разделов Make-программы, которые не
зависят от цели данного правила. Если в этом примере не
указать имя файла, в котором размещена Make-программа, то
автоматически будет выполняться Makefile и будет иметь место
рекурсивный вызов на выполнение одной программы.
Есть некоторые особенности при использовании макропере-
менных. Допустим, в Make-программе указана строка
- 36 -
SRC = a1.c a2.c a3.c # комментарий
Между a3.c и символом # 9 пробелов, они будут передаваться
всюду, где будет использовано значение макропеременной SRC.
Предопределенные макропеременные "<" и "*" в правилах
без суффиксов не определены, и их использование может при-
вести к непредсказуемым результатам.
Все, что указано за символом табуляция в строке дейст-
вий передается на выполнение shell. Однако идущие за симво-
лом табуляция символы "-" и "@" обрабатываются make.
Интерпретатор make оптимизирует скорость выполнения дейст-
вий. Если строка действий - простая команда системы, она
выполняется без порождения процесса shell. По этой причине,
например, такая строка вызовет состояние ошибки
'табуляция'#cat file
Действительно, как бы выполнялась строка "exec(# cat
file);" в Си-программе?
Если в списке зависимостей отсутствуют имена подцелей,
можно использовать сокращенную форму записи правила с одним
действием. Оно имеет вид:
имя_цели:[:]; одна_строка_действия [# комментарий]
символ ";" обязателен.
Особую осторожность необходимо соблюдать при указании в
Make-программе имени цели, которая не является файлом. В
этом случае программист должен учитывать, что он сознательно
исключает возможность использования этой цели при реконст-
рукциях, так как она не связана соотношениями времен созда-
ния (модификации) с другими объектами Make-программы. Это
препятствие можно обойти, создавая ЛОЖНЫЙ файл-цель, напри-
мер:
print: f1 f2 f3
print $?
touch print
.DEFAULT:
touch print
В рабочем каталоге создан пустой файл с именем print. Теперь
выводиться на печать будут только те файлы, которые требу-
ется распечатать как изменившиеся. Правило DEFAULT записано
на тот случай, когда файл print отсутствует.
Команду touch можно использовать, когда необходимо раз-
рушить динамическую структуру связей между файлами. Надо
- 37 -
учитывать, что при этом make будет реконструировать все
файлы заново.
Ниже перечислены все ключи интерпретатора make и их
действие:
-d отладочный режим, в котором выводится дополнительная
информация о выполнении Make-программы.
-f следующий параметр является именем Make-файла. По умол-
чанию ищется Makefile или makefile. Если имеются оба,
то выполняется Makefile. В командной строке можно ука-
зать несколько ключей -f и параметров.
-i режим игнорирования кодов завершения команд не равных
нулю. Эквивалентно директиве IGNORE.
-k если код завершения команды не равен нулю, прекратить
выполнение текущего правила и перейти к выполнению дру-
гих разделов, не зависящих от файла-цели этого правила.
-n вывести, но не выполнять строки действий Make-
программы.
-p вывести полную информацию о структуре Make-программы.
-q получить информацию о необходимости реконструкции цели.
Если реконструкция указанной цели не требуется, возвра-
щается -1, иначе 0.
-r отменяет предопределенную обработку правил с суффик-
сами, предопределенные макропеременные и суффиксы.
-s отменить вывод выполняемых строк. Эквивалентно дирек-
тиве SILENT.
-S прервать выполнение Make-программы при ошибочном завер-
шении какой-либо команды.
-t уничтожить сложившуюся структуру динамических (завися-
щих от времени) связей между файлами.
10. Автоматизация программирования Make-программ
Для создания новой Make-программы можно иметь файл-
шаблон, добавляя в него необходимые строки мы получим гото-
вую к использованию программу. Такой принцип реализован в
программе mkmf. Рассмотрим ее работу на примере. Пусть име-
ются исходные файлы f.h, f1.c, f2.c и f3.c, из которых необ-
ходимо получить файл a.out:
- 38 -
/*
** файл f.h
*/
# include <stdio.h>
# include <ctype.h>
# include <time.h>
/*
** файл f1.c
*/
# include f.h
main(ac, av)
int ac;
char **av;
{
f1(); f2(); f3();
printf("Результат выполнения программы example.\n");
}
f1(){
return;
}
/*
** файл f2.c
*/
# include f.h
int f2(){
return( a2());
}
# include <stat.h>
char *a2(){
return;
}
/*
** файл f3.c
*/
# include f.h
int f3(){
return( nn());
}
char *nn(){
return;
}
Пусть все эти файлы размещены в одном каталоге. Выполним
команду mkmf. В результате ее выполнения будет создан
Makefile с программой сборки файла a.out:
- 39 -
DEST = .
EXTHDRS = /usr/include/ctype.h \
/usr/include/stat.h \
/usr/include/stdio.h \
/usr/include/time.h
HDRS = f.h
LDFLAGS =
LIBS =
LINKER = cc
MAKEFILE = Makefile
OBJS = f1.o f2.o f3.o
PRINT = pr
PROGRAM = a.out
SRCS = f1.c f2.c f3.c
all: $(PROGRAM)
$(PROGRAM): $(OBJS) $(LIBS)
@echo -n "Сборка $(PROGRAM) ..."
@$(LINKER) $(LDFLAGS) $(OBJS) \
$(LIBS) -o $(PROGRAM)
@echo "готово."
clean:; @rm -f $(OBJS)
depend:; @mkmf -f $(MAKEFILE) \
PROGRAM=$(PROGRAM) DEST=$(DEST)
index:; @ctags -wx $(HDRS) $(SRCS)
install: $(PROGRAM)
@echo Установка $(PROGRAM) в $(DEST)
@install -s $(PROGRAM) $(DEST)
print:; @$(PRINT) $(HDRS) $(SRCS)
program:; $(PROGRAM)
tags: $(HDRS) $(SRCS)
@ctags $(HDRS) $(SRCS)
update: $(DEST)/$(PROGRAM)
- 40 -
$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) \
$(HDRS) $(EXTHDRS)
@make -f $(MAKEFILE) \
DEST=$(DEST) install
###
f1.o: f.h /usr/include/stdio.h \
/usr/include/ctype.h \
/usr/include/time.h
f2.o: f.h /usr/include/stdio.h \
/usr/include/ctype.h \
/usr/include/time.h \
/usr/include/stat.h
f3.o: f.h /usr/include/stdio.h \
/usr/include/ctype.h \
/usr/include/time.h
Программой mkmf в качестве исходного файла-шаблона использо-
ван стандартный файл /usr/new/lib/p.Makefile, но можно ука-
зать для использования и любой другой.
Программа mkmf работает следующим образом: сначала
выбираются и вносятся в файл-шаблон имена всех исходных фай-
лов рабочего каталога, далее определяется от каких include-
файлов зависят исходные файлы, формируются правила и записы-
ваются в файл-шаблон. Для обозначения исходных файлов
используются правила с суффиксами.
Makefile можно редактировать, изменять значения макро-
переменных. При этом, если повторить вызов программы mkmf,
в нем появятся только те изменения, которые необходимы для
сборки с учетом изменений в исходных текстах.
В Makefile, полученном из стандартного файла-шаблона,
определены следующие макропеременные:
CFLAGS
ключи Си-компилятора;
DEST
каталог, в котором будет размещен результат;
EXTHDRS
перечень полных имен include-файлов;
HDRS
перечень имен include-файлов, размещенных в рабочем
каталоге;
LIBS
список объектных библиотек для сборки программы;
- 41 -
MAKEFILE
имя файла с Make-программой;
OBJS
список объектных файлов, участвующих в сборке прог-
раммы;
PROGRAM
имя программы, которую необходимо получить;
SRCS
список имен всех файлов с исходными текстами;
Значения макропеременных EXTHDRS, HDRS, OBJS, SRCS
устанавливаются программой mkmf и всегда имеют актуальные
значения. Остальные макропеременные получают при создании
Makefile значения по умолчанию, их можно изменять по своему
усмотрению.
Рассмотрим правила Make-программы, котороые можно
использовать как самостоятельные входы:
all
трансляция, сборка и запуск на выполнение полученной
программы;
clean
удаление ненужных файлов;
depend
изменение структуры Make-программы с учетом существую-
щего Makefile;
index
печать индексов функций собираемой программы;
install
трансляция, сборка и установка программы в указанный
каталог;
print
печать include-файлов и текстов программы;
tags
создание файла ./tags - ссылок программ, написанных на
языках Си, Паскаль и Фортран;
update
изменение Makefile, перегенерация, сборка и установка
программы в указанный каталог. С учетом произошедших
изменений в текстах исходных файлов будет выполнено
только то, что необходимо в данный момент времени.
- 42 -
Пусть имеются файлы f[123].c, f.h и Makefile, заменим в
нем значение макропеременной DEST на /usr/tmp и макропере-
менной PROGRAM - на example. Выполним следующую командную
строку:
% make program install clean
получим на экране сообщение
cc -c f1.c
cc -c f2.c
cc -c f3.c
Сборка example ... готово.
Результат выполнения программы example
Установка example в /usr/tmp
Выполним командную строку
% make index
получим имена функций и места их определений
a2 5 f2.c char *a2(){
f1 11 f1.c f1(){
f2 2 f2.c int f2(){
f3 2 f3.c int f3(){
main 5 f1.c main(ac, av)
nn 6 f3.c char *nn(){
Программа mkmf позволяет создавать Makefile для сборки биб-
лиотеки. Для этого используется файл-шаблон
/usr/new/lib/l.Makefile и дополнительно вводятся макропере-
менная LIBRARY (имя библиотеки) и правила extract (извлече-
ние из библиотеки всех частей в виде отдельных файлов),
library (трансляция и загрузка библиотеки).
Программист может отказаться от стандартных файлов-
шаблонов /usr/new/lib/[lp].Makefile и создать свои шаблоны в
рабочем каталоге. Файлы шаблоны должны иметь имена
l.Makefile и p.Makefile.
- 43 -
ЛИТЕРАТУРА
1. "Банахан М., Раттер Э. Введение в операционную систему
UNIX. - М.: Радио и связь, 1986." AI
2. "Баурн С. Операционная система UNIX.- М.: Мир, 1986." AN
3. "Браун П. Введение в операционную систему UNIX. - М.:
Мир, 1987." AM
4. "Готье Р. Руководство по операционной системе UNIX. -
М.: Финансы и статистика, 1985." AE
5. "Диалоговая единая мобильная операционная система ДЕМОС.
- Калинин: ЦЕНТРПРОГРАММСИСТЕМ, 1985." AK
6. "Инструментальная мобильная операционная система ИНМОС/
М.И. Беляков, А.Ю. Ливеровский, В.П. Семик и др. - М.:
Финансы и статистика, 1985." AH
7. "Керниган Б., Ритчи Д., Фьюер А. Язык программирования
Си. Задачи по языку Си. - М.: Финансы и статистика,
1985." AF
8. "Кристиан К. Введение в операционную систему UNIX. - М.:
Финансы и статистика, 1985." AJ
9. "Хенкок Л., Кригер М. Введение в программирование на
языке СИ. - М.: Радио и связь, 1986." AG
10. "Aho A. V., Kernighan Brian V. W., Weinberger Peter J.
AWK - a pattern scanning and processing language. Second
edition. UNIX Programmers manual, 42 BSD, 1980. Bell
Laboratries: Murray Hill, New Jersey, 1978." AL
11. "Feldman S. I. Make - a program maitaining computer pro-
gramms. Bell Laboratries: Murray Hill, New Jersey,
1978." AB
12. "Joy W. N. An introduction the UNIX C-shell. UNIX Pro-
grammers manual, 42 BSD, 1980. Bell Laboratries: Murray
Hill, New Jersey, 1978." AA
13. "Johnson S. C. YACC - yet another compiler-compiler.
Comp. Sci. tech. rep. N 32. Bell Laboratries: Murray
Hill, New Jersey, 1975." AD
14. "Lesk M. E. Lex - lexical analyzer generator. Comp. Sci.
tech. rep. N 39. Bell Laboratries: Murray Hill, New Jer-
sey, 1975." AC
- 44 -
СОДЕРЖАНИЕ
АННОТАЦИЯ ......................................... 2
1. Введение .......................................... 3
2. Принципы выполнения Make-программы ................ 4
3. Соглашения языка Make ............................. 10
4. Использование макропеременных ..................... 13
5. Выполнение правил в Make-программе ................ 16
6. Режимы выполнения Make-программы .................. 18
7. Правила с суффиксами .............................. 21
8. Управление архивом в Make-программе ............... 30
9. Особенности программирования на языке Make ........ 36
10. Автоматизация программирования Make-программ ...... 38
ЛИТЕРАТУРА ........................................ 44
- 45 -
Last-modified: Mon, 29 Jun 1998 14:13:47 GMT