Вперед Назад Содержание

4. Написание правил

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

Порядок правил несущественнен, за исключением определения главной цели по умолчанию: цели, с которой начинается работа make, если вы ее не определили. По умолчанию главной целью make является цель первого правила в первом make-файле. Если в первом правиле есть несколько целей, то только первая цель берется в качестве главной цели по умолчанию. Есть два исключения: цель, начинающаяся с точки, не является главной целью по умолчанию, если она не содержит при этом один или более символа '/'; кроме того, цель, определяющая шаблонное правило, не воздействует на определение главной цели по умолчанию. Смотрите раздел 10.5 [Определение и переопределение шаблонных правил].

Поэтому обычно make-файл пишется так, чтобы первое правило было правилом для компиляции всей программы или всех программ, описываемых make-файлом (часто с именем цели 'all'). Смотрите раздел 9.2 [Аргументы для определения целей].

4.1 Синтаксис правила

В общем виде, правило имеет следующий вид:

ЦЕЛИ : ЗАВИСИМОСТИ КОМАНДА ...
или такой:

ЦЕЛИ : ЗАВИСИМОСТИ ; КОМАНДА КОМАНДА ...
Слева от двоеточия необходимо указать список имен файлов, разделенных пробелами. При этом могут использоваться шаблонные символы (смотрите раздел 4.2 [Использование шаблонных символов в именах файлов]), а имя в форме 'a(m)' представляет элемент m архивного файла a (Смотрите раздел 11.1 [Элементы архива в качестве целей]). Обычно в правиле присутствует только одна цель, но иногда есть смысл сделать больше (смотрите раздел 4.8 [Несколько целей в правиле]).

Командные строки начинаются с символа табуляции. Первая команда может появится после строки зависимостей, предваренная символом табуляции, или на той же строке, что и зависимости, предваренная символом ';'. Оба способа имеют одинаковый эффект. Смотрите главу 5 [Написание команд в правилах].

Поскольку знак доллара используется в начале переменных, в том случае, если вы хотите поместить сам по себе знак доллара, напишите '$$' (смотрите главу 6 [Как использовать переменные]). Вы можете разбивать длинную строку путем вставки обратной косой чертой, за которой следует перевод строки, однако это не является обязательным требованием, потому что make не устанавливает ограничения на длину строк в make-файле.

Правило несет два вида информации: когда цели находятся в неактуальном состоянии и как, при необходимости, обновить их.

Критерий неактуального состояния определяется в терминах зависимостей, которые состоят из имен файлов, разделенных пробелами. (Допустимы также шаблонные символы и элементы архивов (Смотрите главу 11 [Элементы архива в качестве целей])). Цель считается неактуальной, если она не существует, или она более старая, чем одна из зависимостей (по результатам сревнения времен последних изменений). Идея состоит в том, что содержимое целевого файла вычисляется на основе информации, содержащейся в зависимостях; таким образом, как только любая из зависимостей изменяется, содержимое существующего целевого файла необязательно будет корректнм.

То, как надо обновлять цели, определяется набором команд. Команды представляют собой строки, которые будут выполнены командной оболочкой, но с некоторыми дополнительными возможностями. (Смотрите главу 5 [Написание команд в правилах]).

4.2 Использование шаблонных символов в именах файлов

При использовании шаблонных символов одно имя файла может определять несколько файлов. Шаблонными символами для make являются '*', '?' и '[...]', как и в командной оболочке Bourne shell. Например, '*.c' определяет список всех файлов (в рабочем каталоге), чьи имена заканчиваются на '.c'.

Символ '~' в начале имени файла также имеет специальное значение. Если он один или за ним следует символ '/', он представляет ваш домашний каталог. Например, '~/bin' означает 'home/you/bin'. Если за символом '~' следует слово, строка представляет домашний каталог пользователя, именем которого является это слово. Например, '~john/bin' означает 'home/john/bin'.

Обработка шаблонов автоматически осуществляется в целях, в зависимостях или в командах (где обработку шаблонов осуществляет командная оболочка). В других ситуациях обработка шаблонов производится только тогда, когда явно ее закажете путем использования функции wildcard.

Специальное значение шаблонного символа выключается предшествующей ему обратной косой чертой. Таким образом, 'foo\*bar' ссылается на особый файл, чье имя состоит из 'foo', звездочки и 'bar'.

Примеры шаблонов

Шаблоны могут быть использованы в командах правила, где они обрабатываются командной оболочкой. Вот, например, правило для удаления всех объектных файлов:

clean: rm -f *.o

Шаблоны также полезны в зависимостях правила. При использовании следующего правила в make-файле, 'make print' напечает все '.c'-файлы, которые были изменены с моменты последней их печати:

print: *.c lpr -p $? touch print
Это правило использует 'print' как пустой целевой файл; смотрите раздел 4.6 [Пустые целевые файлы для фиксации событий]. (Автоматическая переменная '$?' используется для печати тех файлов, которые были изменены; смотрите раздел 10.5.3 [Автоматические переменные].)

Обработка шаблонов не осуществляется в момент определения переменной. Таким образом, если вы напишете:

objects = *.o
то значением переменной objects будет именно строка '*.o'. Однако, если вы используете значение этой переменной в цели, зависимости или команде, то при обработке соответствующего правила произойдет обработка шаблона. Чтобы установить в objects результат обработки шаблона, используйте следующую конструкцию:

objects := $(wildcard *.o)

Ловушки в использовании шаблонов

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

objects = *.o foo : $(objects) cc -o foo $(CFLAGS) $(objects)
Значение переменной objects - строка '*.o'. Обработка шаблона происходит в правиле для 'foo', поэтому каждый существующий '.o'-файл становится зависимостью для 'foo' и будет, при необходимости, перекомпилироваться.

Но что будет, если вы удалите все '.o'-файлы? Когда шаблону не соответствует ни один файл, он остается в первозданном виде, и, таким образом, 'foo' будет зависеть от файла со странным именем '*.o'. Поскольку, вероятнее всего, такого файла не существует, make выдаст вам ошибку, говорящую о том, что он не может выяснить, как породить '*.o'. Это не то, чего вы хотите!

На самом деле, достичь желаемого результата при помощи обработки шаблонов возможно, но для этого нужны более развитые методы, включающие в себя функцию wildcard и строковые подстановки. Они описываются в следующем разделе.

Функция wildcard

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

$(wildcard ШАБЛОН...)
Эта строка, будучи использованной в любом месте make-файла, заменяется на разделенный пробелами список имен существующих файлов, соответствующих одному из данных шаблонов имени файла. Если ни один существующий файл не удовлетворяет шаблону, то шаблон не включается в вывод функции wildcard. Обратите внимание, что это отличается от того, как обрабатываются шаблоны без соответствий в правилах make-файла, где они не игнорируются, а используются в первоначальном виде (смотрите раздел 4.2.2 [Ловушки в использовании шаблонов]).

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

$(wildcard *.c)
Мы может заменить список исходных C-файлов на список объектных файлов путем замены в результате функции суффикса '.c' на '.o', как показано ниже:

$(patsubst %.c,%.o,$(wildcard *.c))
(Здесь мы использовали еще одну функцию, patsubst. Смотрите раздел 8.2 [Функции подстановки и анализа строк].)

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

objects := $(patsubst %.c,%.o,$(wildcard *.c)) foo : $(objects) cc -o foo $(objects)
(Здесь используются преимущества неявного правила для компиляции C-программ, поэтому нет необходимости писать явные правила для компиляции файлов. Смотрите раздел 6.2 [Две разновидности переменных] для объяснения знака ':=', являющегося вариантом знака '='.)

4.3 Поиск зависимостей по каталогам

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

VPATH: Путь поиска для всех зависимостей

Значение переменной программы make VPATH определяет список каталогов, в которых следует осуществлять поиск. Чаще всего предполагается, что в этих каталогах содержатся файлы зависимостей, которых нет в текущем каталоге, однако, VPATH определяет список путей поиска, который make применяет ко всем файлам, включая файлы, являющиеся целями правил.

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

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

Например,

VPATH = src:../headers
определяет пути поиска, включающие два каталога, 'src' и '../headers', которые make будет в таком порядке перебирать при поиске.

При этом значении VPATH следующее правило:

foo.o : foo.c
интерпретируется так, как будто оно написано так:

foo.o : src/foo.c
при условии, что файл 'foo' не существует в текущем каталоге, но найден в каталоге 'src'.

Директива vpath

Средством, аналогичным переменной VPATH, но более гибким, является директива vpath (обратите внимание на маленькие буквы), которая позволяет определить путь поиска для определенного класса имен файлов, удовлетворяющих определенному шаблону. Таким образом, вы можете выделить некоторые каталоги поиска для одного класса имен файлов, а другие (или никаких) - для других имен файлов.

Есть три формы директивы VPATH:

vpath ШАБЛОН КАТАЛОГИ

Определяет пути поиска для имен файлов, соответствующих ШАБЛОНу. КАТАЛОГИ представляют собой список каталогов для поиска, разделенный двоеточиями или пробелами, по аналогии с путями поиска, используемыми в переменной VPATH

vpath ШАБЛОН

Очищает пути поиска, связанные с ШАБЛОНом

vpath

Очищает все пути поиска, ранее назначенные директивами vpath

Шаблон vpath является строкой, содержащей символ '%'. Имя файла зависимости, поиск которого осуществляется, должно соответствовать этой строке, причем символ '%' соответствует любой последовательности, содержащей нуль или более символов (как в шаблонных правилах; смотрите раздел 10.5 [Определение и переопределение шаблонных правил]). Например, '%.h' соответствует файлам, которые заканчиваются на .h. (Если нет символа '%', то зависимость должна точно соответствовать шаблону, что бывает нужным не очень часто).

Специальное назначение символа '%' в шаблоне директивы vpath может быть отменено предшествующим символом '\'. Специальное назначение символа '\', который в противном случае отменял бы специальное назначение последующего символа '%', может быть отменено еще одним символом '\'. Символы '\', отменяющие специальное назначение символов '%' или других символов '\', удаляются из шаблона перед тем, как ему будут сопоставляться имена файлов. Символы '\', которые заведемо не влияют на трактовку символа '%', остаются нетронутыми.

Если файл зависимости в текущем каталоге не существует, то в том случае, когда его имя соответствует шаблону из директивы vpath, поиск осуществляется в каталогах, указанных в той директиве, как если бы они были упомянуты в переменной VPATH (причем, поиск в этих каталогах осуществляется перед поиском в тех каталогах, которые на самом деле указаны в переменной VPATH).

Например, строка

vpath %.h ../headers
указывает программе make искать любой файл зависимости, чье имя заканчивается на '.h' в каталоге '../headers', если такой файл не найден в текущем каталоге.

Если имя файла зависимости удовлетворяет нескольким шаблонам vpath, make обрабатывает одну за другой каждую подходящую директиву vpath, осуществляя поиск во всех каталогах, упомянутых в каждой такой директиве. make обрабатывает несколько директив vpath в том порядке, в котором они появляются в make-файле; несколько директив с одинаковым шаблоном не влияют друг на друга.

Таким образом, этот фрагмент

vpath %.c foo vpath % blish vpath %.c bar
означает поиск файла, оканчивающегося на '.c' в каталоге 'foo', затем 'blish', затем 'bar', в то время как этот фрагмент

vpath %.c foo:bar vpath % blish
означает поиск файла, оканчивающегося на '.c' в каталоге 'foo', затем 'bar', затем 'blish'.

Написание команд командной оболочки с учетом поиска по каталогам

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

Это делается с помощью автоматических переменных, таких как '$^' (смотрите раздел 10.5.3 [Автоматические переменные]). Например, значением '$^' является список всех зависимостей правила, включая имена каталогов, в которых они были найдены, а значением '$@ ' - цель. Например:

foo.o : foo.c cc -c $(CFLAGS) $^ -o $@
(Переменная CFLAGS существует для тог, чтобы вы могли определить флаги для C-компиляции посредством неявных правил. Мы используем ее из соображений последовательности, в результате чего она будет одинаково влиять на C-компиляцию. Смотрите раздел 10.3 [Переменные, используемые неявными правилами].)

Часто зависимости также включают в себя заголовочные файлы, которые вы не хотите упоминать в команде. Автоматическая переменная '$<' является просто первой зависимостью.

VPATH = src:../headers foo.o : foo.c defs.h hack.h cc -c $(CFLAGS) $< -o $@

Поиск по каталогам и неявные правила

Поиск в каталогах, определенных в переменной VPATH или при помощи директивы vpath происходит также в случае неявных правил (смотрите главу 10 [Использование неявных правил])

Например, если файл 'foo.o' не имеет явных правил, make рассматривает неявные правила, такие как встроенное правило для компиляции 'foo.c', если такой файл существует. Если такого файла нет в текущем каталоге, то он ищется в соответствующих каталогах для поиска. Если файл 'foo.c' существует (или упоминается в make-файле) в любом из каталогов, применяется неявное правило для C-компиляции.

Командам из неявных правил обычно необходимо пользоваться автоматическими переменными; следовательно, они будут использовать имена файлов, найденных в результате поиска по каталогам без каких-либо дополнительных усилий с вашей стороны.

Поиск по каталогам библиотек для компоновки

Поиск по каталогам библиотек, используемых компоновщиком, применяется особым образом. Эта специфическая особенность вступает в силу, когда вы пишете зависимость, имя которой имеет форму '-l<имя файла>'. (Вы можете сказать, что здесь происходит что-то странное, поскольку зависимость обычно является именем файла, а имя библиотечного файла имеет вид 'lib<имя файла>.a', а не '-l<имя файла>'.)

Когда имя зависимости имеет форму '-l<имя файла>', make специально обрабатывает его, устраивая поиска файла 'lib<имя файла>.a' в текущем каталоге, в каталогах, определенных путями поиска, соответствующими шаблонам директивы vpath и путями поиска из переменной VPATH, а затем в каталогах '/lib', '/usr/lib' и <префикс>/lib (обычно '/usr/local/lib').

Например, правило

foo : foo.c -lcurses cc $^ -o $@
вызовет исполнение команды 'cc foo.c /usr/lib/libcurses.a -o foo', если 'foo' более старый, чем 'foo.c' или '/usr/lib/libcurses.a'.

4.4 Цели-имена действий

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

Если вы пишете правило, команды которого не будут создавать целевой файл, команды будут выполняться каждый раз, когда придет время порождать цель. Вот пример:

clean: rm *.o temp
Поскольку команда rm не создает файл с именем 'clean', вероятно такой файл никогда не будет существовать.

Цель-имя действия прекратит работу, если кто-нибудь когда-нибудь создаст в этом каталоге файл 'clean'. По причине отсутствия зависимостей, файл 'clean' непременно будет считаться свежим, и команды из соответствующего правила не выполняться не будут. Чтобы избежать этой проблемы, вы можете явно объявить цель как имя действия, используя специальную цель .PHONY (смотрите раздел 4.7 [Специальные встроенные имена целей]), как показано ниже:

.PHONY : clean
Как только это сделано, 'make clean' выполнит команды, независимо от того, есть ли файл с именем 'clean'.

Зная, что цель-имя действия не именует настоящий файл, который может быть переделан из других файлов, make пропускает поиск неявного правила для цели-имени действия (смотрите главу 10 [Использование неявных правил]). Именно поэтому объявление цели-имени действия положительно сказывается на производительности, даже если вас не беспокоит возможное существование настоящего файла с таким именем.

Таким образом, вы сначала пишете строку, которая устанавливает clean в качестве цели-имени действия, а затем пишете правило, как показано ниже:

.PHONY: clean clean: rm *.o temp
Цель-имя дествия не должна быть зависимостью реального целевого файла; если это так, ее команды выполняются каждый раз, когда make обновляет этой файл. До тех пор, пока цель-имя действия не является зависимостью никакой реальной цели, ее команды будут исполняться только тогда, когда цель-имя действия определена как главная цель (смотрите раздел 9.2 [Аргументы для определения целей]).

Цели-имена действий могут иметь зависимости. Когда в одном каталоге содержится много программ, наиболее удобно описать все программы в одном make-файле './Makefile'. Поскольку заново порождаемой целью по умолчанию станет первая цель в make-файле, удобно сделать ею цель-имя действия под названием 'all' и дать ей в качестве зависимостей все отдельные программы. Например:

all : prog1 prog2 prog3 .PHONY : all prog1 : prog1.o utils.o cc -o prog1 prog1.o utils.o prog2 : prog2.o cc -o prog2 prog2.o prog3 : prog3.o sort.o utils.o cc -o prog3 prog3.o sort.o utils.o

Теперь вы можете просто набрать в командной строке 'make', чтобы переделать все три программы или определить в качестве аргументов те, что необходимо переделать (например, 'make prog1 prog2').

Когда одна цель-имя действия является зависимостью другой, она служит в качестве ее подпрогораммы. Например, здесь 'make cleanall' удалит объектные файлы, файлы различий и файл 'program'.

.PHONY: cleanall cleanobj cleandiff cleanall : cleanobj cleandiff rm program cleanobj : rm *.o cleandiff : rm *.diff

4.5 Правила без команд и зависимостей

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

Проиллюстрируем это примером:

clean: FORCE rm $(objects) FORCE:
Здесь правило для цели 'FORCE' удовлетворяет указанным условиям, поэтому цель 'clean', зависящая от нее, вынуждена выполнять свои команды. В имени 'FORCE' нет ничего специального, однако это имя часто используется в таких случаях.

Как можно видеть, использование 'FORCE' таким способом дает такой же результат, как и использование '.PHONY : clean'.

Использование '.PHONY' более наглядно и более эффективно. Однако, другие версии make не поддерживают '.PHONY', поэтому 'FORCE' появляется во многих make-файлах. Смотрите раздел 4.4 [Цели-имена действий].

4.6 Пустые целевые файлы для фиксации событий

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

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

Пустой целевой файл должен иметь несколько зависимостей. Когда вы заказываете очередное порождение пустой цели, команды будут выполняться, если какая-нибудь из зависимостей более актуальна, чем цель; другими словами, если зависимость была изменена с того момента, как вы в последний раз породили цель. Вот пример:

print: foo.c bar.c lpr -p $? touch print
Согласно этому правилу, 'make print' выполнит команду lpr, если какой-нибудь исходный файл был изменен с момента последнего 'make print'. Автоматическая переменная '$?' используется для того, чтобы печатать только те файлы, которые изменились (смотрите раздел 10.5.3 [Автоматические переменные]).

4.7 Специальные встроенные имена целей

Некоторые имена, появляясь в качестве целей, имеют специальное значение.

.PHONY

Зависимости специальной цели .PHONY рассматриваются как цели-имена действий. Когда придет время рассматривать такую цель, make выполнит команды в безусловном режиме, независимо от того, существует ли такой файл и от того, какого время его последней модификации. Смотрите раздел 4.4 [Цели-имена действий].

.SUFFIXES

Зависимости специальной цели .SUFFIXES представляют собой список суффиксов, которые будут использоваться при проверке суффиксных правил. Смотрите раздел 10.7 [Устаревшие суффиксные правила].

.DEFAULT

Команды, определенные для .DEFAULT, используются с любыми целями, для которых не найдено правил (как явных, так и неявных). Смотрите раздел 10.6 [Последняя возможность]. Если определены команды для .DEFAULT, то они будут исполняться для каждого файла, упомянутого в качестве зависимости, но не являющегося целью какого-либо правила. Смотрите раздел 10.8 [Алгоритм поиска неявного правила].

.PRECIOUS

Цели, от которых зависит .PRECIOUS, подвергаются специальной обработке: если make уничтожается или прерывается при выполнении соответствующих им команд, цель не удаляется. Смотрите раздел 5.5 [Прерывание или уничтожение программы make]. Кроме того, если цель представляет собой промежуточный файл, он не будет удален после того, как необходимость в нем отпала, как это обычно делается. Смотрите раздел 10.4 [Цепочки неявных правил].

Вы можете также указать шаблон цели неявного правила (как, например, '%.o') в качестве файла зависимости специальной цели .PRECIOUS, чтобы сохранить промежуточные файлы, созданные посредством правил, шаблонам целей которых соответствуют имена этих файлов.

.IGNORE

Если вы определяете зависимости для .IGNORE, то make будет игнорировать ошибки при выполнении команд, запускаемых для этих особых файлов. Команды для .IGNORE роли не играют.

Если .IGNORE определяется как цель без зависимостей, это значит, что необходимо игнорировать ошибки при выполнении команд для всех файлов. Такое использование .IGNORE

поддерживается только для исторической совместимости. Этот прием не очень полезен, поскольку он воздействует на любую команду в make-файле; мы рекомендуем вам использовать более гибкие способы игнорирования ошибок в отдельных командах. Смотрите раздел 5.4 [Ошибки в командах].

.SILENT

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

Если .IGNORE определяется как цель без зависимостей, это значит, что необходимо подавлять печать любой команды перед ее выполнением. Такое использование .SILENT поддерживается только для исторической совместимости. Мы рекомендуем вам использовать более гибкие способы подавления печати перед выполнением отдельных команд. Смотрите раздел 5.1 [Отображение команды]. Если вы хотите подавить печать всех команд при определенном запуске make, используйте опцию '-s' или '--silent' (смотрите раздел 9.7 [Обзор опций]).

.EXPORT_ALL_VARIABLES

Будучи просто упомянутой в качестве цели, указывает программе make по умолчанию экспортировать порожденным процессам все переменные. Смотрите раздел 5.6.2 [Связь порожденным процессом make через переменные].

В качестве специальной цели рассматривается также любой суффикс из определения неявного правила, так же как и конкатенация двух суффиксов, как, например, '.c.o'. Эти цели представляют собой суффиксные правила, устаревший способ определения неявных правил (однако, все еще широко распространенный). В принципе, любое имя цели могло бы таким образом стать специальным, если бы вы разбили его на две части и добавили обе к списку суффиксов. На практике же суффиксы обычно начинаются с '.', поэтому эти специальные имена цели также начинаются с '.'. Смотрите раздел 10.7 [Устаревшие суффиксные правила].

4.8 Несколько целей в правиле

Правило с несколькими целями эквивалентно написанию нескольких правил, каждое из которых имеет одну цель, и идентичных во всем остальном. Ко всем целям применяются одни и те же команды, но их действия могут меняться, поскольку вы можете подставлять в команду конкретное имя цели, используя '$@ '. Правило также распространяет действие всех зависимостей на все цели этого правила.

Это полезно в двух случаях.

Допустим, вы хотели бы изменять зависимости в соответствии с целью аналогично тому, как переменная '$@ ' позволяет вам изменять команды. Вы не можете сделать этого при использовании обычного правила с несколькими целями, но это можно сделать при помощи статического шаблонного правила. Смотрите раздел 4.10 "Статические шаблонные правила".

4.9 Несколько правил для одной цели

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

Для файла может исполнен только один набор команд. Если более, чем одно правило дает команды для одного и того же файла, make использует последний встретившийся набор команд и выдает сообщение об ошибке. (Как исключение, если имя файла начинается с точки, сообщение об ошибке не печатается. Такое странное поведение поддерживается только для совместимости с другими реализациями make). У вас нет причин писать make-файлы таким образом, поэтому make выдает вам сообщение об ошибке.

Дополнительное правило, содержащее только зависимости, может быть использовано для добавления нескольких дополнительных зависимостей одновременно к нескольким файлам. Например, обычно имеется переменная с именем objects, содержащая список всех файлов, являющихся выходом компилятора в порождаемой системе. Легкий способ указать, что все они должны быть перекомпилированы, если изменился файл 'config.h' - написать следующее :

objects = foo.o bar.o foo.o : defs.h bar.o : defs.h test.h $(objects) : config.h
Это могло быть вставлено или убрано без изменения правил, которые действительно определяют, как порождать объектные файлы, что является удобным для использования способом, если вы хотите в произвольном месте добавлять дополнительные зависимости.

Еще один полезный совет состоит в том, что дополнительные зависимости могут быть определены при помощи переменной, которую вы устанавливаете в аргументе командной строки для make. (смотрите раздел 9.5 [Перекрывающиеся переменные]). Например, правило

extradeps= $(objects) : $(extradeps)
означает, что команда 'make extradeps=foo.h' будет рассматривать 'foo.h' в качестве зависимости для каждого объектного файла, а просто 'make' - не будет.

Если ни одно из явных правил для цели не имеет команд, то make организует поиск применимого неявного правила, чтобы найти какие-нибудь команды (смотрите главу 10 [Использование неявных правил]).

4.10 Статические шаблонные правила

Статические шаблонные правила - это правила, которые определяют несколько целей и создают имена зависимостей для каждой цели на основе имени цели. Они являются более общими, чем обычные правила с несколькими целями, поскольку цели не должны иметь одинаковые зависимости. Их цели должны быть похожими, но не обязательно одинаковыми.

Синтаксис статических шаблонных правил

Здесь приведен синтаксис статического шаблонного правила:

ЦЕЛИ ...: ШАБЛОН ЦЕЛИ: ШАБЛОНЫ ЗАВИСИМОСТЕЙ ... КОМАНДЫ ...
Список ЦЕЛЕЙ определяет цели, к которым применяется правило. Цели могут содержать шаблонные символы, так же как и цели обычных правил (смотрите раздел 4.2 [Использование шаблонных символов в именах файлов]).

ШАБЛОН ЦЕЛИ и ШАБЛОНЫ ЗАВИСИМОСТЕЙ указывают, как вычислять зависимости каждой цели. К каждой цели применяется шаблон цели для получения части имени цели, называемой основой. Эта основа подставляется в каждый из шаблонов зависимостей, в результате чего порождаются имена зависимостей (по одному из каждого шаблона зависимости).

Обычно каждый шаблон содержит ровно один символ '%'. Когда цель сопоставляется шаблону цели, символ '%' соответствует незафиксированной части имени цели - эта часть называется основой. Оставшаяся часть шаблона должна точно соответствовать имени цели. Например, цель 'foo.o' удовлетворяет шаблону '%.o', при этом 'foo' является основой. Цели 'foo.c' и 'foo.out' не удовлетворяют шаблону.

Имена зависимостей для каждой цели соэдаются путем подстановки основы вместо символа '%' в каждый шаблон зависимости. Например, если единственный шаблон зависимости - '%.c', то подстановка основы 'foo' дает имя зависимости 'foo.c'. Является законным написание шаблона зависимости, не содержащего '%' - в таком случае данная зависимость одинакова для всех целей.

Специальное назначение символа '%' в шаблоне правила может быть отменено предшествующим символом '\'. Специальное назначение символа '\', который в противном случае отменял бы специальное назначение последующего символа '%', может быть отменено еще одним символом '\'. Символы '\', отменяющие специальное назначение символов '%' или других символов '\', удаляются из шаблона перед тем, как он будет сравниваться с именами файлов или в него будет подставляться основа. Символы '\', которые заведомо не влияют на трактовку символа '%', остаются нетронутыми. Например, в шаблоне 'the\%weird\\%pattern\\', фрагмент 'the%weird\' предшествует действующему символу '%', а фрагмент 'pattern\\' следует за ним. Последние два символа '\' остаются на месте, поскольку они не могут воздействовать ни на какой символ '%'.

Вот пример, который компилирует или 'foo.o' или 'bar.o' из соответствующего '.c'-файла:

objects = foo.o bar.o $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
Здесь '$<' является автоматической переменной, которая содержит имя зависимости, а '$@ ' - автоматической переменной которая содержит имя цели; смотрите раздел 10.5.3 [Автоматические переменные].

Каждая специфицированная цель должна соответствовать шаблону цели - для каждой цели, которая не соответствует, выдается предупреждение. Если у вас есть список файлов, из которого только некоторые будут соответствовать шаблону, вы можете использовать функцию filter для отсечения несоответствующих имен файлов (смотрите раздел 8.2 [Функции подстановки и анализа строк]):

files = foo.elc bar.o lose.o $(filter %.o,$(files)): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ $(filter %.elc,$(files)): %.elc: %.el emacs -f batch-byte-compile $<
В этом примере результатом '$(filter %.o,$(files))' является 'bar.o lose.o', и первое статическое шаблонное правило вызывает обновление каждого из этих объектных файлов путем компиляции соответствующих исходных 'C'-файлов. Результатом '$(filter %.elc,$(files))' является 'foo.elc', поэтому этот файл порождается из 'foo.el'.

Еще один пример показывает, как использовать '$*' в статических шаблонных правилах:

bigoutput littleoutput : %output : text.g generate text.g -$* > $@

Статические шаблонные правила в сравнении с неявными правилами

Статические шаблонные правила имеют много общего с неявными правилами, определенными как шаблонные правила (смотрите раздел 10.5 [Определение и переопределение шаблонных правил]). В обоих случаях имеется шаблон для цели и шаблоны для построения имен зависимостей. Различие заключается в том, как make определяет, когда применяются правила.

Неявное правило может быть применено к любой цели, которая соответствует его шаблону, но оно применяется только тогда, когда цель не имеет команд, определенных иным способом и когда могут быть найдены зависимости. Если применяемым оказывается более одного неявного правила, применяется только одно - его выбор зависит от порядка правил.

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

Статическое шаблонное правило может быть предпочтительнее неявного правила по следующим причинам:

4.11 Правила с двумя двоеточиями

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

Когда цель появляется в нескольких правилах, все правила должны быть одного типа: все обычные или все с двойным двоеточием. Если они с двойным двоеточием, то каждое из них не зависит от других. Команды каждого правила с двойным двоеточием выполняются, если цель обновлялась раньше, чем какая-либо зависимость из этого правила. Это может привести к выполнению любого или всех из правил с двойным двоеточием, а также к невыполнению ни одного такого правила.

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

Правила с двойным двоеточием для цели выполняются в том порядке, в котором они появляются в make-файле. Однако, в тех случаях, когда действительно есть смысл в правилах с двойным двоеточием, порядок выполнения команд не играет роли.

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

Каждое правило с двойным двоеточием должно определять команды - если они не определены, будет использовано неявное правило, в случае его применимости. Смотрите главу 10 [Использование неявных правил].

4.12 Автоматическая генерация зависимостей

В make-файле для программы, многие из правил, которые вам приходится писать, указывают только на то, что некоторый объектный файл зависит от некоторого заголовочного файлов. Например, если 'main.c' использует 'defs.h' посредством директивы #include, вы бы написали:

main.o: defs.h
Вам это правило нужно для того, чтобы make знал, что он должен обновлять 'main.o' всегда, когда изменяется 'defs.h'. Можно заметить, что для большой программы вы бы написали в вашем make-файле десятки таких правил. Кроме того, вы всегда должны быть очень внимательны в отношении обновления make-файла каждый раз, когда вы добавляете или удаляете директиву #include.

Чтобы избавиться от этого неудобства, большинство современных C-компиляторов могут написать для вас эти правила, просмотрев в исходном файле строки с директивой #include. Обычно это делается при помощи опции компилятора '-M'. Например, команда:

cc -M main.c
генерирует на выходе:

main.o : main.c defs.h
Таким образом, вам больше не требуется самим писать все такие правила. Компилятор сделает это за вас.

Обратите внимание, что такая зависимость порождает упоминание 'main.o' в make-файле, таким образом он впоследствии не может рассматриваться как промежуточный файл при поиске неявного правила. Это означает, что make никогда не будет удалять файл после его использования - смотрите раздел 10.4 [Цепочки неявных правил].

В старых программах make традиционной практикой было использование возможности компилятора генерировать зависимости с помощью команды вида 'make depend'. Эта команда создавала файл 'depend', содержащий все автоматически генерируемые зависимости - в таком случае make-файл мог использовать директиву include для их чтения (смотрите раздел 3.3 [Включение]).

В GNU-версии программы make из-за возможности обновления make-файлов такая практика становится устаревшей - вам никогда не требуется явно указывать программе make перегенерировать зависимости, поскольку она всегда перегенерирует любой make-файл, который является необновленым. Смотрите раздел 3.5 [Как переделываются make-файлы].

Рекомендуемая нами практика автоматической генерации зависимостей заключается в том, чтобы иметь для каждого исходного файла соответствующий make-файл. Для каждого исходного файла '<имя файла>.c' заводится make-файл '<имя файла>.d', в котором перечисляются файлы, от которых зависит объектный файл '<имя файла>.o'. В таком случае для порождения новых зависимостей требуется заново просмотреть только измененный файл.

Вот шаблонное правило для порождения файла зависимостей (т.е. make-файла) с именем '<имя файла>.d' из исходного C-файла с именем '<имя файла>.c':

%.d: %.c $(SHELL) -ec '$(CC) -M $(CPPFLAGS) $< \ | sed '\''s/$*\\.o[ :]*/& $@/g'\'' > $@'
Смотрите раздел 10.5 [Шаблонные правила] для информации об определении шаблонных правил. Благодаря флагу командной оболочки '-e', непосредственно после неудачного выполнения команды $(CC) (завершения с ненулевым результатом) происходит завершение работы оболочки. В противном случае командная оболочка завершалась бы с результатом последней команды в конвейере (в данном случае sed), поэтому программа make не замечала бы ненулевой результат компилятора.

При использовании компилятора GNU C вы можете захотеть вместо флага '-M' использовать флаг '-MM'. Его использование приводит к пропуску зависимостей от системных заголовочных файлов. Подробности смотрите в разделе "Опции управления препроцессором" руководства по использованию GNU CC.

Предназначением программы sed является преобразование (например):

main.o : main.c defs.h
в:

main.o main.d : main.c defs.h

Это делает каждый '.d'-файл зависимым от всех исходных и заголовочных файлов, от которых зависит соответствующий '.o'-файл. В этом случае make знает, что всегда, когда изменяется любой из исходных или заголовочных файлов, требуется перегенерировать зависимости.

После того, как вы определили правило для обновления '.d'-файлов, вы используете директиву include для чтения их всех. Смотрите раздел 3.3 [Включение]. Например:

sources = foo.c bar.c include $(sources:.c=.d)
(В этом примере для преобразования списка исходных файлов 'foo.c bar.c' в список make-файлов с зависимостями 'foo.d bar.d' используется ссылка на переменную с заменой. Смотрите раздел 6.3.1 [Cсылки с заменой] для полной информации о подстановочных ссылках). Так как '.d'-являются такими же make-файлами, как и любые другие, make при необходимости обновит их без какой-либо дополнительной работы с вашей стороны. Смотрите раздел 3.5 [Как переделываются make-файлы].


Вперед Назад Содержание