Страница:
$? - список подцелей : 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 Структура $@ изменилась.;\
$@ - имя цели : Документы
Папка : справки, копии.
Справка с места жительства.
Копия свидетельства о рождении.
Действия правила библиотека
$? - список подцелей : ПОЛКА Документы
$@ - имя цели : библиотека
Техническая : английский, немецкий.
Худ. : проза, поэзия, драматургия.
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 Структура $@ изменилась.;\