Документ взят из кэша поисковой машины. Адрес оригинального документа : http://mavr.sao.ru/hq/sts/linux/doc/infocity/GNU_Make_3-79_russian_manual.html
Дата изменения: Unknown
Дата индексирования: Fri Dec 28 19:52:33 2007
Кодировка: koi8-r

Поисковые слова: п п п п п п п п п п п п п п п п п р п р п р п р п р п р п р п р п р п
GNU make

GNU Make

Программа управления компиляцией

GNU make Версия 3.79

Апрель 2000

Richard M. Stallman и Roland McGrath, перевод (C) Владимир Игнатов, 2000

Версия перевода 0.1

Английский оригинал этого текста находится здесь.

Оригинал перевода находится на моей домашней страничке.


Оглавление


Назначение программы make

Утилита make автоматически определяет какие части большой программы должны быть перекомпилированы, и выполняет необходимые для этого действия. В данном руководстве описывается программа GNU make, авторами которой являются Richard Stallman и Roland McGrath. Начиная с версии 3.76, разработку программы ведет Paul D. Smith.

GNU make удовлетворяет требованиям раздела 6.2 стандарта IEEE Standard 1003.2-1992 (POSIX.2).

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

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

После того, как нужный make-файл создан, простой команды :

make

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

При вызове make, в командной строке могут быть заданы параметры, указывающие, какие файлы следует перекомпилировать и каким образом это делать. Смотрите раздел Запуск make.

Как читать данное руководство

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

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

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

Ошибки и проблемы

Если у вас возникли проблемы с использованием GNU make или вам кажется, что вы обнаружили ошибку в ее работе, пожалуйста, сообщите об этом разработчикам. Мы не может обещать невозможного, однако постараемся исправить положение.

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

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

Если вы действительно обнаружили проблему, пожалуйста, сообщите нам об этом по следующему адресу:

    bug-make@gnu.org

Пожалуйста, укажите номер версии вашей программы make. Вы можете получить эту информацию, набрав в командной строке `make --version'. Не забудьте также указать тип вашей машины и операционной системы. По возможности, укажите содержимое файла `config.h', который получается в результате работы процесса конфигурирования на вашей машине.

Знакомство с make-файлами (makefiles)

Для работы с утилитой make, вам понадобится так называемый make-файл (makefile), который будет содержать описание требуемых действий. Как правило, make-файл описывает, каким образом нужно компилировать и компоновать программу.

В этой главе мы обсудим простой make-файл, который описывает, как скомпилировать и скомпоновать программу - текстовой редактор. Наш текстовой редактор будет состоять из восьми файлов с исходными текстами на языке Си и трех заголовочных файлов. Make-файл также может инструктировать make как выполнять те или иные действия, когда явно будет затребовано их выполнение (например, удалить определенные файлы в ответ на команду "очистка"). Пример более сложного make-файла будет приведен в разделе Пример "сложного" make-файла.

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

Как выглядят правила (rules)

Простой make-файл состоит из "правил" (rules) следующего вида:

цель ... : пререквизит ...
        команда
        ...
        ...

Обычно, цель (target) представляет собой имя файла, который генерируется в процессе работы утилиты make. Примером могут служить объектные и исполняемый файлы собираемой программы. Цель также может быть именем некоторого действия, которое нужно выполнить (например, `clean' - очистить). Подробнее это обсуждает в разделе Абстрактные цели ).

Пререквизит (prerequisite) - это файл, который используется как исходдные данные для порождения цели. Очень часто цель зависит сразу от нескольких файлов.

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

Обычно, команды находятся в правилах с пререквизитами и служат для создания файла-цели, если какой-нибудь из пререквизитов был модефицирован. Однако, правило, имеющее команды, не обязательно должно иметь пререквизиты. Например, правило с целью `clean' ("очистка"), содержащее команды удаления, может не иметь пререквизитов.

Правило (rule) описывает, когда и каким образом следует обновлять файлы, указанные в нем в качестве цели. Для создания или обновления цели, make исполняет указанные в правиле команды, используя пререквизиты в качестве исходных данных. Правило также может описывать каким образом должно выполняться некоторое действие. Подробно это обсуждается в разделе Составление правил.

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

Пример простого make-файла

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

В данном примере, заголовочный файл `defs.h' включается во все файлы с исходным текстом. Заголовочный файл `command.h' включается только в те исходные файлы, которые относятся к командам редактирования, а файл `buffer.h' - только в "низкоуровневые" файлы, непосредственно оперирующие буфером редактирования.

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

Для повышения удобочитаемости, мы разбили длинные строки на две части с помощью символа обратной косой черты, за которым следует перевод строки.

Для того, чтобы с помощью этого make-файла создать исполняемый файл `edit', наберите:

make

Для того, чтобы удалить исполняемый и объектные файлы из директории проекта, наберите:

make clean

В приведенном примере, целями, в частности, являются объектные файлы `main.o' и `kbd.o', а также исполняемый файл `edit'. К пререквизитам относятся такие файлы, как `main.c' и `defs.h'. Каждый объектный файл, фактически, является одновременно и целью и пререквизитом. Примерами команд могут служить `cc -c main.c' и `cc -c kbd.c'.

В случае, если цель является файлом, этот файл должен быть перекомпилирован или перекомпонован всякий раз, когда был изменен какой-либо из его пререквизитов. Кроме того, любые пререквизиты, которые сами генерируются автоматически, должны быть обновлены первыми. В нашем примере, исполняемый файл `edit' зависит от восьми объектных файлов; объектный файл `main.o' зависит от исходного файла `main.c' и заголовочного файла `defs.h'.

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

Цель `clean' является не файлом, а именем действия. Поскольку, при обычной сборке программы это действие не требуется, цель `clean' не является пререквизитом какого-либо из правил. Следовательно, make не будет "трогать" это правило, пока вы специально об этом не попросите. Заметьте, что это правило не только не является пререквизитом, но и само не содержит каких-либо пререквизитов. Таким образом, единственное предназначение данного правила - выполнение указанных в нем команд. Цели, которые являются не файлами, а именами действий называются абстрактными целями (phony targets). Абстрактные цели подробно рассматриваются в разделе Абстрактные цели. В разделе Ошибки при выполнении команд описано, как заставить make игнорировать ошибки, которые могут возникнуть при выполнении команды rm и любых других команд.

Как make обрабатывает make-файл

По умолчанию, make начинает свою работу с первой встреченной цели (кроме целей, чье имя начинается с символа `.'). Эта цель будет являться главной целью по умолчанию (default goal). Главная цель (goal) - это цель, которую стремится достичь make в качестве результата своей работы. В разделе Аргументы для задания главной цели обсуждается, каким образом можно явно задать главную цель.

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

Таким образом, когда вы даете команду:

make

make читает make-файл из текущей директории и начинает его обработку с первого встреченного правила. В нашем примере это правило обеспечивает перекомпоновку исполняемого файла `edit'. Однако, прежде чем make сможет полностью обработать это правило, ей нужно обработать правила для всех файлов, от которых зависит `edit'. В данном случае - от всех объектных файлов программы. Каждый из этих объектных файлов обрабатывается согласно своему собственному правилу. Эти правила говорят, что каждый файл с расширением `.o' (объектный файл) получается в результате компиляции соответствующего ему исходного файла. Такая компиляция должна быть выполнена, если исходный файл или какой-либо из заголовочных файлов, перечисленных в качестве пререквизитов, являются "более новыми", чем объектный файл, либо объектного файла вообще не существует.

Другие правила обрабатывается потому, что их цели прямо или косвенно являются пререквизитами для главной цели. Если какое-либо правило никоим образом не "связано" с главной целью (то есть ни прямо, ни косвенно не являются его пререквизитом), то это правило не обрабатывается. Чтобы задействовать такие правила, придется явно указать make на необходимость их обработки (подобным, например, образом: make clean).

Перед перекомпиляцией объектного файла, make рассматривает необходимость обновления его пререквизитов, в данном случае - файла с исходным текстом и заголовочных файлов. В нашем make-файле не содержится никаких инструкций по обновлению этих файлов - файлы с расширениями `.c' и `.h' не являются целями каких-либо правил. Таким образом, утилита make не предпринимает никаких действий с этими файлами. Однако, make могла бы автоматически обновлять и исходные тексты, если бы они, например, генерировались с помощью программ, подобных Bison или Yacc, и для них были бы определены соответствующие правила.

После перекомпиляции объектных файлов, которые нуждаются в этом, make принимает решение - нужно ли перекомпоновывать файл `edit'. Это нужно делать, если файла `edit' не существует или какой-нибудь из объектных файлов по сравнению с ним является более "свежим". Если какой-либо из объектных файлов только что был откомпилирован заново, то он будет "моложе", чем файл `edit'. Соответственно, файл `edit' будет перекомпонован.

Так, если мы модефицируем файл `insert.c' и запустим make, этот файл будет скомпилирован заново для обновления объектного файла `insert.o', и, затем, файл `edit' будет перекомпонован. Если мы изменим файл `command.h' и запустим make, то будут перекомпилированы объектные файлы `kbd.o', `command.o' и `files.o', а затем исполняемый файл `edit' будет скомпонован заново.

Упрощение make-файла с помощью переменных

В приведенном выше примере, в правиле для `edit' нам дважды пришлось перечислять список объектных файлов программы:

edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

Подобное дублирование чревато ошибками. При добавлении в проект нового объектного файла, можно добавить его в один список и забыть про другой. Мы можем устранить подобный риск, и, одновременно, упростить make-файл, используя переменные. Переменные (variables) позволяют, один раз определив текстовую строку, затем использовать ее многократно в нужных местах. Переменные подробно обсуждаются в разделе Использование переменных).

Обычной практикой при построении make-файлов является использование переменной с именем objects, OBJECTS, objs, OBJS, obj, или OBJ, которая содержит список всех объектных файлов программы. Мы могли бы определить подобную переменную с именем objects таким образом:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

Далее, всякий раз, когда нам нужен будет список объектных файлов, мы можем использовать значение этой переменной с помощью записи `$(objects)' (смотрите раздел Использование переменных).

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

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit $(objects)

Неявные правила упрощают make-файл

На самом деле, нет необходимости явного указания команд компиляции отдельно для каждого из исходных файлов. Утилита make сама может "догадаться" об использовании нужных команд, поскольку у нее имеется, так называемое, неявное правило (implicit rule) для обновления файлов с расширением `.o' из файлов с расширеним `.c', с помощью команды `cc -c'. Например, она бы использовала команду `cc -c main.c -o main.o' для преобразования файла `main.c' в файл `main.o'. Таким образом, можно убрать явное указание команд компиляции из правил, описывающих построение объектных файлов. Смотрите раздел Использование неявных правил.

Когда файл с расширением `.c' автоматически используется подобным образом, он также автоматически добавляется в список пререквизитов "своего" объектного файла. Таким образом, мы вполне можем убрать файлы с расширением `.c' из списков пререквизитов объектных файлов.

Вот новый вариант нашего примера, в который были внесены оба описанных выше изменения, а также используется переменная objects:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
        -rm edit $(objects)

Примерно так и выглядят make-файлы в реальной практике. Для правила с `clean' здесь использована более сложная запись, которую мы обсудим позже (смотрите разделы Абстрактные цели, и Ошибки при исполнении команд).

Из-за своего удобства, неявные правила широко используются и играют важную роль в работе make.

Другой стиль написания make-файлов

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

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

Здесь, заголовочный файл `defs.h' объявляется пререквизитом для всех объектных файлов программы. Файлы `command.h' и `buffer.h' являются пререквизитами для перечисленных объектных файлов.

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

Правило для очистки каталога

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

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

clean:
        rm edit $(objects)

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

.PHONY : clean
clean :
        -rm edit $(objects)

Такая запись предотвратит возможную путаницу если, вдруг, в каталоге будет находится файл с именем `clean', а также позволит make продолжить работу, даже если команда rm завершится с ошибкой (смотрите раздел Абстрактные цели, а также раздел Ошибки при исполнении команд.)

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

Поскольку clean не является пререквизитом цели edit, это правило не будеть выполняться, если вызывать `make' без аргументов. Для запуска данного правила, нужно будет набрать `make clean'. Смотрите раздел Запуск make.

Создание make-файлов

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

Из чего состоят make-файлы

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

Имена make-файлов

По умолчанию, когда make ищет make-файл для обработки, она поочередно пробует найти файлы со следующими именами (в указанном порядке): `GNUmakefile', `makefile' и `Makefile'.

Обычно, вам имеет смысл давать своему make-файлу имя `makefile', либо `Makefile'. Мы рекомендуем использовать имя `Makefile', потому что при выводе содержимого каталога, файл с таким именем будет находится в начале списка, наряду с такими важными файлами как `README'. Первое из проверяемых имен - `GNUmakefile' - не может быть рекомендовано для большинсства make-файлов. Это имя можно использовать, если ваш make-файл специфичен для GNU make и не будет обрабатываться другими версиями make. Другие версии программы make ищут make-файлы с именами `makefile' и `Makefile', но не `GNUmakefile'.

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

Если вы хотите использовать "нестандартное" имя для вашего make-файла, вы можете указать его в командной строке, используя опции `-f' или `--file'. Аргументы `-f имя_файла' или `--file=имя_файла', указывают программе make на необходимость использования файла с именем имя_файла в качестве make-файла. Вы можете задать обработку сразу нескольких make-файлов, перечислив их в командной строке с помощь нескольких опций `-f' или `--file'. Все указанные таким образом make-файлы логически "объединяются" в том порядке, как они были заданы в командной строке. При наличии в командной строке опций `-f' или `--file', автоматического поиска make-файлов с именами `GNUmakefile', `makefile' и `Makefile', не производится.

Подключение других make-файлов

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

include имена_файлов...

Имена файлов могут представлять собой шаблоны имен, допустимые в интерпретаторе командной строки.

В начале строки могут находится дополнительные пробелы - все они будут игнорированы. Наличие символа табуляции в начале строки недопустимо, поскольку такие строки make считает командами. Между словом include и началом списка файлов, а также между именами файлов необходим пробел. Лишние пробелы между именами, а также пробелы после конца директивы, игнорируются. В конце строки с директивой может находится комментарий, начинающийся, как обычно, с символа `#'. Если имена файлов содержат ссылки на переменные или функции, то эти ссылки "раскрываются" (вместо них подставляются вычисленные значения). Смотрите раздел Использование переменных.

Если, например, у вас есть три файла с расширением `.mk' - `a.mk', `b.mk', и `c.mk', а переменная $(bar) ссылается на строку bish bash, то следующая запись

include foo *.mk $(bar)

будет эквивалентна

include foo a.mk b.mk c.mk bish bash

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

Директива include может оказаться полезной если, предположим, у нас имеется несколько программ, собираемых при помощи отдельных make-файлов, которым требуется наличие некоторого "общего" набора определений переменных (смотрите раздел Установка значения переменной) или шаблонных правил (смотрите раздел Определение и переопределение шаблонных правил).

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

Если указанное в директиве имя начинается не с символа '/' и файл с таким именем отсутствует в текущей директории, производится его поиск еще в нескольких каталогах. Сначала поиск производится во всех каталогах, которые были указаны в командной строке с помощью опций `-I' и `--include-dir' (смотрите раздел Обзор опций). Затем, поиск производится поочередно в следующих директориях (если, конечно, они существуют): `prefix/include' (обычно `/usr/local/include' (1)) `/usr/gnu/include', `/usr/local/include', `/usr/include'.

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

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

-include имена_файлов...

Эта директива работает аналогично директиве include, за исключением того, что отсутствие включаемых make-файлов имена_файлов не вызывает ошибки (даже не выдается каких-либо предупреждающих сообщений). Для совместимости с другими версиями make, директива -include имеет второе, дополнительное имя sinclude.

Переменная MAKEFILES

Если среди переменных среды (environment variables) имеется переменная с именем MAKEFILES, то ее содержимое интерпретируется как список имен (разделенных пробелами) дополнительных make-файлов, которые должны быть прочитаны перед тем, как начнут обрабатываться "основные" make-файлы. Этот механизм работает во многом аналогично директиве include. Аналогичным образом производится и поиск этих дополнительных make-файлов в разных каталогах (смотрите раздел Подключение других make-файлов). При этом, главная цель не может браться из этих файлов, а отсутствие какого-либо из них не рассматривается как ошибка.

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

Некоторые пользователя соблазняются возможностью автоматически устанавливать переменную MAKEFILES при входе в систему, и пишут свои make-файлы в рассчете на это. Делать этого категорически не рекомендуется, поскольку такие make-файлы не будут работать при попытке их использования другими пользователями. Гораздо лучше, явно подключать нужные make-файлы с помощью обычной директивы include. Смотрите раздел Подключение других make-файлов .

Автоматическое обновление make-файлов

Иногда make-файлы могут быть получены из других файлов, таких как файлы RCS или SCCS. Если make-файл может быть получен из других файлов, скорее всего, вы захотите, чтобы make всегда работала с самой "свежей" версией этого файла.

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

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

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

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

Поэтому, если ни один из make-файлов с принятыми по умолчанию именами не существует, make предпринимает попытку создать их (в том же самом порядке, каком происходил их поиск). Смотрите раздел Имена make-файлов . Эти попытки продолжаются до тех пор, пока какой-либо из make-файлов не будет создан, либо make "перепробует" все имена make-файлов, принятых по умолчанию. Заметьте, что невозможность найти или построить make-файл не является ошибкой, поскольку наличие make-файла не является обязательным условием работы make.

При использовании опций `-t' или `--touch' (смотрите раздел Вместо исполнения команд), вряд ли вы захотите оказаться в ситуации, когда для определения того, какие цели нужно пометить как обновленные, будет использована устаревшая версия make-файла. Поэтому, опция `-t' не оказывает влияния на процедуру обновления make-файлов - они обновляются даже тогда, когда она указана. Аналогично, опции `-q' (или `--question') и `-n' (или `--just-print') не отменяют процедуру обновления файлов; в противном случае, использование устаревшей версии make-файла могло бы вызвать некорректную работу этих опций. Таким образом, при обработке `make -f mfile -n foo', файл `mfile' будет обновлен, затем он будет перечитан заново, и, после этого, будут напечатаны команды, обновляющие цель `foo' и ее пререквизиты. Эти команды будут соответствовать работе с обновленной версией `mfile'.

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

Таким образом, при обработке `make -f mfile -n mfile foo' будет прочитан make-файл `mfile', затем будут напечатаны команды, требуемые для его обновления (без их реального исполнения), и, затем, будут напечатаны команды, необходимые для обновления `foo' (также без их выполнения). Команды для обновления `foo' будут соответствовать нынешнему состоянию `mfile'.

"Перекрытие" (overriding) части make-файла

Иногда, возникает потребность в нескольких make-файлах, лишь незначительно различающихся между собой. Зачастую, в такой ситуации может быть использована директива `include': нужный make-файлы можно получить путем включения другого make-файла и добавления своего набора правил или определений переменных. Однако, с помощью подобной методики, вам не удастся задать разные команды для обновления одной и той же цели. Этого можно добиться другим способом.

Во "внешнем" make-файле (make-файле, который включает в себя другие make-файлы) вы можете задать шаблонное правило с произвольным соответствием (match-anything pattern rule), которое будет указывать, что цели, не описанные в данном make-файле, следут поискать в другом make-файле. Смотрите раздел Определение и переопределение шаблонных правил, где подробно описаны шаблонные правила.

Например, если у нас имеется make-файл с именем `Makefile', который описывает цель `foo' (и другие цели), то мы можем написать make-файл с именем `GNUmakefile', который будет содержать следующие строки:

foo:
        frobnicate > foo

%: force
        @$(MAKE) -f Makefile $@
force: ;

В таком случае, при выполнении команды `make foo', make считает файл `GNUmakefile', и увидит, что для достижения цели `foo', должна быть выполнена команда `frobnicate > foo'. При выполнении команды `make bar', make увидит, что цель `bar' не описана в make-файле `GNUmakefile', и, поэтому, использует команду из шаблонного правила: `make -f Makefile bar'. Если `Makefile' содержит правило для цели `bar', то эта цель будет обновлена. Аналогично make поступит и с любыми другими целями, не описанными в `GNUmakefile'

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

Как make читает make-файл

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

Понимание такой двухпроходной схемы является важным, поскольку она оказывает непосредственное влияние на ход вычисления переменных и функций; непонимание, зачастую, является источником недоразумений при написании make-файлов. Здесь мы опишем, как происходит "пофазная" обработка различных конструкций. Мы будем говорить, что расширение (expansion) является немедленным (immediate), если оно производится во время первой фазы работы: это означает, make будет вычислять переменные и функции в момент считывания и "разбора" make-файла. Мы будем говорить, что расширение является отложенным (deferred), если оно не происходит "немедленно". Расширение отложенной конструкции не происходит до тех пор, пока эта конструкция не встретится позже, уже в "немедленном" контексте, либо она будет расширена на втором проходе.

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

Присваивание значения переменным

Определения переменных обрабатываются следующим образом:

немедленно = отложенно
немедленно ?= отложенно
немедленно := немедленно
немедленно += отложенно или немедленно

define немедленно
  отложенно
endef

В операторе добавления, `+=', правая часть обрабатывается "немедленно", если переменная была ранее определена как упрощенно вычисляемая (с помощью `:=') и "отложенно" в противном случае.

Условные конструкции

Все условные конструкции (во всех формах - ifdef, ifeq, ifndef и ifneq) целиком и полностью обрабатываются "немедленно".

Определения правил

Правила всегда обрабатываются одинаковым образом, независимо от их формы:

немедленно : немедленно ; отложенно
    отложенно

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

Составление правил (rules)

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

Порядок следования правил внутри make-файле не имеет значения. Исключение составляет лишь выбор главной цели по умолчанию (default goal) - цели, к которой стремиться make, если вы не задали ее явно. По умолчанию, главной целью становиться цель из первого правила в первом обрабатываемом make-файле. Если это правило содержит несколько целей, то только первая из них становится главной целью. Здесь есть два исключения. Во-первых, главными целями, выбираемыми по умолчанию, не могут стать цели, имя которых начинается с точки (если только они не содержат по крайней мере одного символа `/'). И, во-вторых, из процесса выбора главной цели исключаются шаблонные правила (смотрите раздел Определение и переопределение шаблонных правил).

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

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

В общем виде, правило выглядит так:

цели : пререквизиты
        команда
        ...

или так:

цели : пререквизиты ; команда
        команда
        ...

Цели (targets) - это имена файлов, разделенные пробелами. В именах целей могут быть использованы шаблонные символы (смотрите раздел Использование шаблонных символов в именах файлов). Для файлов, содержащихся в архиве, может быть использована специальная форма записи: `a(m)', где a - это имя архивного файла, а m - имя содержащегося в нем файла (смотрите раздел Использование элементов архива в качестве целей). Обычно, в правиле содержится только одна цель, однако, иногда имеет смысл задать несколько целей в одном правиле (смотрите раздел Правила с несколькими целями).

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

Поскольку знак доллара используется для ссылки на переменные, для использования его в правилах, нужно писать `$$' (смотрите раздел Использование переменных). Длинные строки make-файла могут быть разделены на части с помощью символа '\', находящегося в конце строки. Это может повысить удобочитаемость make-файла, но "технической" необходимости в этом нет - make никак не ограничивает длину строк make-файла.

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

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

Команды указывают на то, каким образом следует обновлять цель. Это - просто строки (с некоторыми дополнительными возможностями), исполняемые интерпретатором командной строки (обычно `sh'). Смотрите раздел Написание команд.

Использование шаблонных символов (wildcard characters) в именах файлов

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

Символ `~' в начале имени файла, также имеет специальное значение. Одиночный символ `~' или сочетание `~/' означает ваш домашний каталог. Например, выражение `~/bin' будет означать `/home/you/bin'. Если сразу за символом `~' следует некоторое имя, такая строка будет представлять собой домашнюю директорию пользователя с этим именем. Например, строка `~john/bin' будет означать `/home/john/bin'. В системах, где пользователи не имеют своего домашнего каталога (таких как MS-DOS или MS-Windows), такое поведение может эмулироваться с помощью установки переменной окружения HOME.

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

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

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

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

clean:
        rm -f *.o

Шаблоны также могут быть полезны в качестве пререквизитов правил. В следующем примере, команда `make print' вызовет печать всех файлов с исходными текстами (файлов с расширением `.c'), которые были модефицированы с тех пор, как вы последний раз распечатывали их подобным образом:

print: *.c
        lpr -p $?
        touch print

В данном правиле, цель `print' является пустой целью (empty target file); смотрите раздел Использование пустых целей для фиксации событий. Автоматичесая переменная `$?' используется для печати только тех пререквизитов, которые были изменены (смотрите раздел Автоматические переменные.)

При задании переменной, раскрытия шаблонов не производится. Например, если вы запишите:

objects = *.o

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

objects := $(wildcard *.o)

Смотрите раздел Функция wildcard.

Проблемы при использовании шаблонных имен

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

objects = *.o

foo : $(objects)
        cc -o foo $(CFLAGS) $(objects)

При такой записи, переменная objects получит значение `*.o'. Расширение шаблона `*.o' будет произведено только при обработке правила с `foo', и пререквизитами этой цели станут все существующие в данный момент файлы `.o'. При необходимости, эти объектные файлы будут перекомпилированы.

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

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

В операционных системах фирмы Microsoft для разделения имен директорий используется символ '\':

  c:\foo\bar\baz.c

Приведенное выше имя эквивалентно имени `c:/foo/bar/baz.c' в стиле Unix (здесь `c:' - это, так называемое, имя диска). Когда программа make работает в таких операционных системах, она допускает использование обоих символов ('/' и '\') в именах файлов. Поддержка символа '\' не распространяется, однако, на шаблонные имена, где этот символ имеет специальное значение. В таких случаях, вы должны использовать имена в стиле Unix (с символом '/' в качестве "разделителя").

Функция wildcard

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

$(wildcard шаблон...)

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

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

$(wildcard *.c)

Затем, мы может превратить список исходных файлов в список объектных файлов, заменив их расширение с `.c' на `.o', например:

$(patsubst %.c,%.o,$(wildcard *.c))

(Здесь, для замены текста, мы использовали функцию patsubst. Смотрите раздел Функции анализа и подстановки строк.)

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

objects := $(patsubst %.c,%.o,$(wildcard *.c))

foo : $(objects)
        cc -o foo $(objects)

В этом make-файле для компиляции исходных текстов используются неявные правила компиляции программ на языке Си, поэтому нет необходимости в явном описании правил компиляции. Смотрите раздел Две разновидности переменных, где описывается оператор `:=', который является вариантом "стандартного" оператора `='.

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

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

Переменная VPATH: список каталогов для поиска пререквизитов

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

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

Имена перечисленных в VPATH каталогов, отделяются друг от друга пробелами или двоеточиями. При поиске, make перебирает каталоги в том порядке, как они перечислены в переменной VPATH. В операционных системах MS-DOS и MS-Windows для разделения имен директорий вместо символа двоеточия, должен использоваться символ точки с запятой (поскольку символ ':' может быть частью названия каталога, находясь после имени диска).

Например, следующая запись:

VPATH = src:../headers

указывает, что путь поиска состоит из двух каталогов, `src' и `../headers'. Поиск в этих каталогах производится в указанном порядке.

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

foo.o : foo.c

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

foo.o : src/foo.c

если предположить, что файл `foo.c' находится не в текущей директории, а в каталоге `src'.

Директива vpath

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

Имеется три формы записи директивы vpath:

vpath шаблон каталоги
Задать путь поиска каталоги для файлов, чье имя удовлетворяет шаблону шаблон. Путь поиска каталоги - это список имен директорий, разделенных двоеточиями (точкой запятой в MS-DOS или MS-Windows) или пробелами, подобно списку поиска переменной VPATH.
vpath шаблон
Очистить ("забыть") пути поиска для шаблона шаблон.
vpath
Очистить ("забыть") все пути поиска, ранее определенные с помощью директивы vpath

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

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

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

Например, запись

vpath %.h ../headers

инструктирует make, производить поиск пререквизитов с расширением `.h' в каталоге `../headers', если они не могут быть найдены в текущей директории.

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

Например, в случае:

vpath %.c foo
vpath %   blish
vpath %.c bar

поиск файла с раширением `.c' будет происходить в каталоге `foo', затем `blish', и, наконец `bar', а в случае

vpath %.c foo:bar
vpath %   blish

поиск такого файла будет производиться в каталоге `foo', затем `bar', и затем `blish'.

Процедура поиска по каталогам

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

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

  1. Если целевой файл не может быть найден в директории, которая указана в make-файле, проводится его поиск по каталогам.
  2. Если поиск завершился успешно, найденный путь запоминается и искомый целевой файл временно помечается как найденная цель.
  3. Используя этот же метод, проверяются все пререквизиты данной цели.
  4. После обработки всех пререквизитов, для цели, возможно потребуется ее обновление:
    1. Если цель не нуждается в обновлении, директория, найденная в процессе поиска по каталогам, используется для всех пререквизитов этой цели. Иначе говоря, если цель не нуждается в обновлении, то используется директория, найденная в процессе поиска по каталогам.
    2. Если цель нуждается в обновлении (является устаревшей), то найденный в процессе поиска по каталогам путь отбрасывается, и цель обновляется с использовании только ее имени, указанном в make-файле. Другими словами, если make должна обновить цель, то эта цель строится или обновляется "локально", а не в той директории, которая была найдена во время поиска по каталогам.

Хотя этот алгоритм и выглядит сложным, на практике, он, как правило, дает именно тот результат, который вы и ожидали.

Другии версии make используют более простой алгоритм: если файла нет в текущей директории, но он был найден в процесс поиска по каталогам, установленный таким образом путь к этому файлу используется всегда, независимо от того, нуждается ли цель в обновлении или нет. Таким образом, при обновлении цели, новая версия файла будет расположена по тому же пути, где была найдена и "старая" версия.

Если для вас желательно именно такое поведение make по отношению к некоторым (или всем) каталогам, вы можете использовать переменную GPATH.

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

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

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

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

foo.o : foo.c
        cc -c $(CFLAGS) $^ -o $@

(Переменная CFLAGS используется для того, чтобы иметь возможность указать опции компиляции исходных текстов на Си для неявных правил; в данном случае мы использовали ее просто в целях "унификации" процесса компиляции (смотрите раздел Используемые в неявных правилах переменные).

Часто, в список пререквизитов попадают файлы, которые не нужно передавать в исполняемую команду (например, заголовочные файлы). В такой ситуации можно использовать автоматическую переменную `$<', которая содержит лишь первый пререквизит правила:

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

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

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

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

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

Поиск в каталогах для подключаемых библиотек

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

Когда имя пререквизита имеет форму `-lимя', make обрабатывает ее специальным образом, производя поиск файла с именем `libимя.so' сначала в текущей директории, затем в каталогах, перечисленных в подходящих директивах vpath, каталогах из VPATH, и, наконец, в каталогах `/lib', `/usr/lib', и `prefix/lib' (обычно `/usr/local/lib', но версии make для операционных систем MS-DOS/MS-Windows ведут себя так, как если бы prefix был корневым каталогом, где инсталлирован компилятор DJGPP).

Если такой файл не обнаружен, предпринимается попытка найти файл `libимя.a' (в перечисленных выше каталогах).

Так, если в вашей системе имеется библиотека `/usr/lib/libcurses.a' (и отсутствует файл `/usr/lib/libcurses.so'), то в следующем примере:

foo : foo.c -lcurses
        cc $^ -o $@

будет выполнена команда `cc foo.c /usr/lib/libcurses.a -o foo' если `foo' "старше" чем `foo.c' или `/usr/lib/libcurses.a'.

Хотя, по умолчанию проводится поиск файлов с именами `libимя.so' и `libимя.a', это поведение может быть изменено с помощью переменной .LIBPATTERNS. Каждое слово в значении этой переменной рассматривается как шаблонная строка. Встретив пререквизит вида `-lимя', make заменяет символ процента в каждом из шаблонов на имя и производит описанную выше процедуру поиска для полученного имени библиотечного файла. Если библиотечный файл не найден, используется следующий шаблон из списка и так далее.

По умолчанию, значением переменной .LIBPATTERNS является строка "`lib%.so lib%.a'", которая и обеспечивает описанное выше поведение.

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

Абстрактные цели (phony targets)

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

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

clean:
        rm *.o temp

Поскольку исполнение команды rm не приводит к созданию файла `clean', такой файл, скорее всего, вообще не будет существовать. В таком случае, команда rm будет выполняться всякий раз, когда вы скажете `make clean'.

Однако, правило с такой "псевдо-целью" откажется работать, если в текущем каталоге по какой-нибудь причине окажется файл с именем `clean'. Поскольку в правиле не указано каких-либо пререквизитов, файл `clean' всегда будет считаться "новым" и команды, указанные в правиле никогда не выполнятся. Во избежании подобной проблемы, вы можете прямо указать, что некоторая цель является абстрактной. Для этого используется специальная цель .PHONY (смотрите раздел Имена специальных целей). В нашем примере достаточно записать:

.PHONY : clean

После этого, вызов `make clean' будет приводить к исполнению нужных команд, независимо от того, существует файл `clean' или нет.

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

Таким образом, сначала должна идти строка, объявляющая clean абстрактной целью, а затем уже следует правило, описывающее эту цель:

.PHONY: clean
clean:
        rm *.o temp

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

SUBDIRS = foo bar baz

subdirs:
        for dir in $(SUBDIRS); do \
          $(MAKE) -C $$dir; \
        done

Такому методу, однако, присущи некоторые недостатки. Во-первых, любые ошибки, возникшие при обработке подпроектов, останутся "незамеченными" - при возникновении ошибки в подпроекте make будет продолжать обработку оставшихся подкаталогов "как ни в чем ни бывало". Разумеется, в цикл можно ввести дополнительный код, который будет детектировать ошибочные ситуации и прерывать работу. К сожалению, при запуске make с опцией -k, такое поведение будет нежелательно. Второй недостаток, (возможно, более серьезный) состоит в том, что при таком подходе нельзя задействовать возможность "параллельной" работы make (из-за наличия единственного правила).

Объявив подкаталоги абстрактными целями (вы должны это сделать так как подкаталоги проектов обычно уже существуют и иначе они не стали бы обрабатываться) вы можете решить эти проблемы:

SUBDIRS = foo bar baz

.PHONY: subdirs $(SUBDIRS)

subdirs: $(SUBDIRS)

$(SUBDIRS):
        $(MAKE) -C $

foo: baz

Мы также объявили, что подкаталог `foo' не может быть обработан до тех пор, пока не будет закончена обработка подкаталога `baz'; подобного рода декларация потребуется для случая "параллельной" работы.

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

Абстрактные цели могут иметь пререквизиты. Например, когда в одном каталоге содержится сразу несколько собираемых программ, удобно хранить их описания в одном make-файле. Так как главной целью по умолчанию становится первая цель из make-файла, в таких случаях, обычно, первым правилом 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 prog3').

Когда одна абстрактная цель является пререквизитом другой абстрактной цели, она работает как своего рода "подпрограмма". В следующем примере, `make cleanall' удалит объектные файлы, diff-файлы, и файл `program':

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
        rm program

cleanobj :
        rm *.o

cleandiff :
        rm *.diff

Правила без команд и пререквизитов

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

Например:

clean: FORCE
        rm $(objects)
FORCE:

Здесь, цель `FORCE' удовлетворяет специальным условиям (не имеет пререквизитов и команд). Цель `clean' зависит от `FORCE', поэтому команды из правила с `clean' вынуждены будут выполняться. В имени `FORCE' нет ничего "необычного", просто оно часто используется для подобных целей.

Очевидно, что такое использование `FORCE' эквивалентно объявлению цели clean абстрактной, с помощью `.PHONY: clean'.

Подход с использованием `.PHONY' более понятен и эффективен, однако другие версии make могут не поддерживать `.PHONY'. В силу этой причины, во многих make-файлах используется `FORCE'. Смотрите раздел Абстрактные цели.

Использование пустых целей (empty target files) для фиксации событий

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

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

Пустая цель должна иметь какие-нибудь пререквизиты (иначе в ней нет смысла). Когда вы запрашиваете обновление этой цели, команды из ее правила будут выполняться, если какой-либо из пререквизитов "новее", чем цель. Другими словами, команды будут выполняться если какой-либо из пререквизитов был обновлен со времени последнего обновления цели. Например:

print: foo.c bar.c
        lpr -p $?
        touch print

С таким правилом, `make print' приведет к выполнению команды lpr, если какой-нибудь из исходных файлов был изменен с момента последнего вызова `make print'. Автоматическая переменная `$?' использована для того, чтобы печатать только те исходные файлы, которые были изменены (смотрите раздел Автоматические переменные).

Имена специальных целей

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

.PHONY
Пререквизиты специальной цели .PHONY объявляются абстрактными целями. При необходимости обновления таких целей, make будет выполнять команды "безусловно", независимо от того, существует ли файл с таким именем, и времени, когда он был модефицирован. Смотрите раздел Абстрактные цели.
.SUFFIXES
Пререквизиты специальной цели .SUFFIXES представляют собой список суффиксов (расширений) имен файлов, которые будут использоваться при поиске суффиксных правил. Смотрите раздел Устаревшие суффиксные правила.
.DEFAULT
Команды, определенные для цели .DEFAULT, будут использованы со всеми целями make-файла, для которых не найдено ни явных, ни неявных правил. Смотрите раздел Определение правил "последнего шанса". Команды, определенные для .DEFAULT, будут использованы для всех пререквизитов, не являющихся целями каких-либо правил. Смотрите раздел Алгоритм поиска неявных правил.
.PRECIOUS
Цели, перечисленные в качестве пререквизитов .PRECIOUS, подвергаются специальной обработке. В том случае, если make будет принудительно завершена или прервана во время исполнения команд для их обновления, эти цели не будут удалены. Смотрите раздел Прерывание или принудительное завершение make. Также, если цель является "промежуточным" файлов, он не будет, как обычно, удаляться после того, как необходимость в нем отпала. Смотрите раздел "Цепочки" неявных правил. В последнем случае, эта специальная цель работает подобно .SECONDARY. В качестве пререквизита .PRECIOUS может быть указан шаблон имени (например, `%.o'), что позволит сохранять все промежуточные файлы с именами, удовлетворяющими этому шаблону.
.INTERMEDIATE
Пререквизиты цели .INTERMEDIATE рассматриваются как промежуточные файлы. Смотрите раздел "Цепочки" неявных правил. .INTERMEDIATE без списка пререквизитов не производит никакого эффекта.
.SECONDARY
Цели, указанные в качестве пререквизитов .SECONDARY рассматриваются как промежуточные файлы, за исключением того, что они никогда не удаляются автоматически. Смотрите раздел "Цепочки" неявных правил. .SECONDARY без указания пререквизитов, помечает таким образом все цели, перечисленные в make-файле.
.DELETE_ON_ERROR
При наличии в make-файле цели с именем .DELETE_ON_ERROR, make будет удалять цель правила если она была модефицированы, а обновляющая ее команда завершилась с ненулевым кодом возврата; аналогично, цель будет удаляться при прерывании работы make. Смотрите раздел Ошибки при исполнении команд.
.IGNORE
make будет игнорировать ошибки при выполнении команд, обновляющих цели, перечисленные в качестве пререквизитов .IGNORE. Команды, указываемые для .IGNORE, не имеют значения. Использование .IGNORE без списка пререквизитов, означает необходимость игнорирования ошибок во всех командах, исполняемых для обновления любой цели make-файла. Такое использование `.IGNORE' поддерживается только по историческим соображениям для обеспечения совместимости. Этот прием не слишком полезен, поскольку воздействует на любую команду make-файла; вместо этого, мы рекомендуем использовать более "избирательный" метод, позволяющий игнорировать ошибки в конкретных командах. Смотрите раздел Ошибки при исполнении команд.
.SILENT
Если вы указали некоторые цели в качестве пререквизитов .SILENT, то в процессе обновления этих целей, make не будет печатать выполняемые при этом команды. Указываемые для .SILENT команды не имеют значения. В случае использования .SILENT без списка пререквизитов, будет отключена печать всех исполняемых команд. Такое использование `.SILENT' поддерживается только по историческим причинам, для обеспечения совместимости. Мы рекомендуем использовать более избирательный метод для подавления печати отдельных команд. Смотрите раздел Отображение исполняемых команд. Временно подавить печать исполняемых команд можно, запуская make с опциями `-s' или `--silent' (смотрите раздел Обзор опций).
.EXPORT_ALL_VARIABLES
Будучи просто упомянутой в качестве цели, указывает make на необходимость, по умолчанию, экспортировать все переменные для возможности их использования в дочернем процессе. Смотрите раздел Связь с make "нижнего уровня" через переменные.
.NOTPARALLEL
При наличии в make-файле цели .NOTPARALLEL, данный экземпляр make будет работать "последовательно" (даже при наличии опции `-j'). Рекурсивно запущенные экземпляры make по-прежнему могут работать "параллельно" (если только их make-файлы не содержат такой же специальной цели). Любые пререквизиты данной цели игнорируются.

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

Правила с несколькими целями

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

Это может быть полезно в двух случаях.

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

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

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

Для одной цели может быть исполнен только один набор команд. Если команды для обновления цели указаны сразу в нескольких правилах, 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-файл или удалена из него, не затрагивая "основные" правила, используемые для генерации объектных файлов. Это удобно при необходимости "срочно" добавить в make-файл еще несколько пререквизитов.

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

extradeps=
$(objects) : $(extradeps)

вызов `make extradeps=foo.h' будет добавлять `foo.h' в список пререквизитов каждого из объектных файлов. При обычном вызове `make', этого делаться не будет.

Если ни одно из правил, описывающее цель, не имеет команд, make попытается применить к этой цели неявные правила (смотрите раздел Использование неявных правил).

Статические шаблонные правила (static pattern rules)

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

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

Для статических шаблонных правил используется следующий синтаксис:

цели ...: шаблон-цели: шаблоны-пререквизитов ...
        команды
        ...

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

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

Обычно, в каждом шаблоне содержится по одному символу `%'. Когда цель сопоставляется с шаблоном-цели, символ `%' может соответствовать любой части имени цели; именно эта часть будет являться основой. Прочие части имени цели должны в точности совпадать с шаблоном. Например, цель `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

all: $(objects)

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

В этом примере, автоматическая переменные `$<' и `$@' содержат, соответственно, имя пререквизита и имя цели (смотрите раздел Автоматические переменные).

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

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', и первое статическое правило вызывает компиляцию этих объектных файлов из соответствующих им исходных файлов. Результатом выражения `$(filter %.elc,$(files))' является `foo.elc', и этот файл получается из `foo.el'.

Следующий пример иллюстрирует использование автоматической переменной $* в статическом шаблонном правиле:

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

При запуске команды generate, ссылка на $* будет заменена соответствующей основой имени - строкой `big' или `little'.

Сравнение статических шаблонных правил (static pattern rules) и неявных правил (implicit rules)

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

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

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

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

Правила с двойным двоеточием (double-colon rules)

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

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

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

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

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

Каждое правило с двойным двоеточием должно содержать команды, в противном случае make попытается применить подходящее неявное правило. Смотрите раздел Использование неявных правил.

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

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

main.o: defs.h

Это правило необходимо для того, чтобы make обновляла объектный файл `main.o' при каждом изменении `defs.h'. Для большой программы, скорее всего, вам придется написать большое количество подобных правил. Далее, каждый раз при изменении исходных текстов путем добавления или удаления директивы #include, вам придется модефицировать соответствующим образом и make-файл.

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

cc -M main.c

будет строка:

main.o : main.c defs.h

Таким образом, вам больше не потребуется писать подобные правила "вручную" - эту работу возьмет на себя компилятор.

Обратите внимание, что в приведенной выше зависимости упоминается файл `main.o' и, следовательно, этот файл не сможет рассматриваться как промежуточный в процессе поиска неявных правил. Как следствие, make никогда не будет удалять этот файл после его использования; смотрите раздел "Цепочки" неявных правил.

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

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

Мы рекомендуем вам использовать подход, когда для каждого исходного файла имеется свой маленький make-файл, содержащий список пререквизитов этого исходного файла. Для каждого исходного файла `имя.c' имеется make-файл `имя.d', в котором перечислен список файлов, от которых зависит объектный файл `name.o'. При таком подходе, новые списки пререквизитов могут строиться только для тех исходных файлов, которые действительно были модефицированы.

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

%.d: %.c
        set -e; $(CC) -M $(CPPFLAGS) $< \
                  | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
                [ -s $@ ] || rm -f $@

Для получения подробной информации об определении шаблонных правил смотрите раздел Определение и переопределение шаблонных правил. Флажок `-e' заставляет интерпретатор командной строки немедленно завершать работу в случае, если при вызове $(CC) произойдет ошибка (команда возвратит отличный от нуля код завершения). Обычно, интерпретатор командной строки завершается с кодом возврата, полученным от последней выполненной команды из всей цепочки команд (sed в данном случае) и make может просто "не заметить" ошибочной ситуации, произошедшей при вызове компилятора.

При использовании компилятора GNU C, вместо опции `-M' вы можете попробовать использовать опцию `-MM'. При этом, в список пререквизитов не будут попадать системные заголовочные файлы. Смотрите раздел `Options Controlling the Preprocessor' руководства по компилятору GNU C.

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

main.o : main.c defs.h

в строку:

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

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

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

sources = foo.c bar.c

include $(sources:.c=.d)

(В этом примере, для преобразования списка исходных файлов `foo.c bar.c' в список пререквизитов (`foo.d bar.d'), используется техника ссылки на переменную с подстановкой. Смотрите раздел Ссылка с заменой). Поскольку файлы с расширением `.d' являются полноправными make-файлами, утилита make будет сама заботится об их своевременном обновлении, не требуя от вас каких-либо дополнительных усилий. Смотрите раздел Автоматическое обновление make-файлов.

Написание команд

Определенн