Быстрое введение в GNU Make.

Первоисточник http://linux.oreillynet.com/pub/a/linux/2002/01/31/make_intro.html

by Jennifer Vesperman
01/31/2002

Make изначально появилась как система для building компилированного кода. Но, как и любой хороший инструмент, система нашла широкое применение и в других областях. Ее использование оправдано всякий раз, когда изменение в одном файле требует изменений (или действий) в других файлах или каталогах.

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

Running Make

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

"Стандартное" имя такого файла - Makefile. Обратите внимание на регистр первой буквы, это "отличительное" свойство для специальных файлов, типа README и других.

Когда make введено без параметров, GNU Make ищет конфигурационные файлы с именами GNUmakefile, Makefile и makefile в текущем каталоге. Если вы используете файл с другим именем, следует вызывать Make как make -f filename.

Следующие примеры makefile достаточно полно демонстрирует возможности make и при этом не перегружены. По умолчанию выполняется список команд. Обратите внимание на вызов с ключом make -s.


$ls
makefile    renamed_makefile

$make
echo make complete
make complete

$make -f renamed_makefile
echo make complete
make complete

$make -s
make complete

Простой Makefiles.

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

Опытные пользователи Make заметят избыточность в makefile примера. Make заранее знает, как компилировать некоторые типы файлов, и правила для таких файлов могут не описываться (так называемые неявные правила [implicit rules]). В демонстрационных целях такие правила указаны явно.


# Linking object files
sample: main.o example.o
		cc -o sample main.o example.o
		echo sample: make complete

# Compiling source files
main.o: main.c main.h
		cc -c main.c
example.o: example.c defs.h
		cc -c example.c

Make правило представляет собой:


target: prerequisites
	commands

Атрибуты генерируемого файла [target] просматриваются на предмет "up to date" и, если файл-предпосылка [prerequisites] "более свежий" или target не существует, выполняется команда.

Make работает с использованием рекурсивного отката, начиная с target первого правила. В нашем примере это sample. Make проверяет prerequisites для sample (в нашем случае main.o и example.o) чтобы увидеть, имеются ли правила для них. Если такие правила есть, рекурсивно проверяются правила для этих правил...

Make "раскручивает" вниз цепочку рекурсий пока не достигает "дна", то есть находит target, которая не имеет prerequisites или чьи prerequisites не имеют rules. Как только это происходит, начинается откат с копированием цепочки рекурсий и выполнением команд по необходимости. Иначе, создается цепочка рекурсий для каждой prerequisite, с которой сталкивается make (и для которой существует rule).

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

В случае с makefile из примера:

  1. Находится первое правило, это - sample.
  2. Выясняется, имеется ли у sample's prerequisites правила. Есть и не одно.
  3. Находится правило для первой prerequisite - main.o.
  4. Выясняется, имеется ли у main.o's prerequisites правила. Их нет.
  5. Проверяется, является ли main.o "современным". Если нет, выполняется команда для генерирования main.o.
  6. Находится правило для второй prerequisite - example.o.
  7. Выясняется, имеется ли у example.o's prerequisites правила. Их нет.
  8. Проверяется, является ли example.o "современным". Если нет, выполняется команда для генерирования example.o.
  9. Возврат к правилу sample's.
  10. Проверяется, является ли sample "современным". Если нет, выполняется команда для его модификации.

Make может выполнять prerequisites в любом порядке. Принципиально только одно - рекурсивный откат от первой target (или от target, переданной команде make в виде параметра) и проверка только тех правил, которые попали в цепочку предпосылок.

Make обрывает компиляцию при возникновении ошибки. Такое поведение очень полезно. Оно позволяет исправлять обнаруженные компилятором проблемы в цикле "compile-and-test" без лишних повторов. Ключ -i сообщает make необходимость игнорировать ошибки.

Фоновые Targets

В мире software development очень удобно иметь сценарий, который удаляет код старой компиляции и дает возможность пере собрать программу полностью. Кроме того, не будет лишним иметь сценарий для инсталляции. Make позволяет included подобные сценарии в makefile через phony targets. Фальшивые targets могут иметь prerequisites либо сами могут быть prerequisites.

Специальное правило .PHONY сообщает Make, что ее targets не являются файлами. Таким способом предотвращаются возможные конфликты с одноименными файлами, что позволяет "не думать о мелочах".

Если phony target включена как prerequisite для другой target, она будет выполняться при каждом вызове основной. Иначе, для phony target понятия "never up-to-date" не существует.

Чтобы запустить phony target из командной строки, введите Make с именем phony target, например: make clean.


# Naming our phony targets
.PHONY: clean install

# Removing the executable and the object files
clean:
		rm sample main.o example.o
		echo clean: make complete

# Installing the final product
install:
		cp sample /usr/local
		echo install: make complete
 

Переменные Makefile.

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

Синтаксис для объявления и установки makefile variable следующий: varname = variable contents. Для вызова переменной используется $(varname).


# Defining the object files
objects = main.o example.o

# Linking object files
sample: $(objects)
		cc -o sample $(objects)
		echo sample: make complete

# Compiling source files
main.o: main.c main.h
		cc -c main.c
example.o: example.c defs.h
		cc -c example.c

# Removing the executable and the object files
clean:
		rm sample $(objects)
		echo clean: make complete

Последний штрих.

Имеется несколько мелочей, которые отличают "просто пригодный к использованию" и "профессионально сделанный" makefile. Следующие строки - дополнение к сказанному.


# 1
# Defining the compiler:
CC=gcc

# Defining the object files:
objects = main.o example.o

# 2
# The default rule - compiling our main program:
all:	sample
		echo all: make complete

# 3
sample: $(objects)
	# If we get here, all the dependencies are now built.
	# Link it:
	$(CC) -o $@ $+

# 4
# Tell make how to build .o files from .c files:
%.o:%.c
	$(CC) -c $+

# 5
#Now make sure that make rebuilds files if included headers change:
main.o: main.h defs.h
example.o: example.h defs.h
  1. Используйте variable для объявления компилятора. Это позволит быстро и безошибочно переделать makefile под другой компилятор.

  2. При вызове Make без rule parameter, выполняется первое встретившееся правило. Чтобы "облегчить жизнь" тех, кто будет просматривать созданный вами makefile, принято помечать первое rule. Имя all - "общепринятый стандарт" для первого правила.

  3. "Автоматическая" переменная $@ означает "имя target", automatic variable $+ означает "все prerequisites разделенные пробелом". Такие переменные предопределены в Make.

  4. Подстановки в правилах используется для сообщения Make как обращаться с именами файлов при конвертации prerequisite в target. Символ % в pattern буквально означает "один или несколько символов", а применительно к строке, содержащей prerequisite и target - использовать одно имя с разными суффиксами. Такой специфический pattern сообщает make конвертировать *.c файлы в *.o файлы не меняя имен.

  5. Использование "неявных правил" [implicit rules]. Make имеет встроенные инструкции [built-in patterns] для конвертирования *.h файлов в зависимые *.o файлы. Такие правила included для объявления prerequisites для уместных *.o файлов.

Примечание.

Статья описывает GNU Make. В некоторых Unix системах используются другие версии Make, что предполагает возможные незначительные отличия.

Заключение.

Make имеет множество features, о которых здесь не упоминалось, и которые могут быть очень полезны в отдельных случаях (например, когда зависимости [dependent] файлов могут изменяться). Экспериментируйте и вносите свои интересные features для работы с makefiles.

Further Reading

Jennifer Vesperman likes to think she was born with a silicon wafer attached to her spinal column, but she can't get her parents to admit it. She contributes to open source, mostly as a user and an advocate. Jenn is the current coordinator for Linuxchix.org.


Перевод: Vladimir Kholmanov