Структура make-файлов
Утилита make
,
автоматически определяет какие части большой программы должны быть
перекомпилированы, и выполняет необходимые для этого действия.
Перед тем, как использовать make
, необходимо создать так называемый make-файл (makefile),
который будет описывать зависимости между файлами компилируемой программы, и содержать команды для обновления
этих файлов. Как правило, исполняемый файл программы зависит от объектных файлов, которые, в свою очередь,
получаются в результате компиляции соответствующих файлов с исходными текстами.
После того, как нужный make-файл создан, простой команды :
make
будет достаточно для выполнения всех необходимых перекомпиляций если какие-либо из исходных файлов программы были изменены. Используя информацию из make-файла, и, зная время последней модефикации файлов, утилита make решает, каких из файлов должны быть обновлены. Для каждого из этих файлов будут выполнены указанные в make-файле команды.
При вызове make
, в командной строке могут быть заданы параметры, указывающие,
какие файлы следует перекомпилировать и каким образом это делать.
Простой make-файл
Простой make-файл состоит из "правил" (rules) следующего вида:
цель ... : пререквизит ...
команда
...
...
Обычно, цель (target
) представляет собой имя файла, который генерируется в процессе работы утилиты make
.
Примером могут служить объектные и исполняемый файлы собираемой программы. Цель также может быть именем
некоторого действия, которое нужно выполнить (например, clean
- очистить).
Пререквизит (prerequisite
) - это файл, который используется как исходдные данные для порождения цели.
Очень часто цель зависит сразу от нескольких файлов.
Команда - это действие, выполняемое утилитой make
. В правиле может содержаться несколько команд -
каждая на свое собственной строке. Важное замечание: строки, содержащие команды обязательно должны начинаться с символа табуляции!
Игнорирование табцляции это наиболее часто встречающаяся ошибка многих начинающих пользователей.
Обычно, команды находятся в правилах с пререквизитами и служат для создания файла-цели,
если какой-нибудь из пререквизитов был модефицирован. Однако, правило, имеющее команды,
не обязательно должно иметь пререквизиты. Например, правило с целью clean
("очистка"), содержащее команды удаления, может не иметь пререквизитов.
Правило (rule
) описывает, когда и каким образом следует обновлять файлы, указанные в нем в качестве цели.
Для создания или обновления цели, 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 и любых других команд.
Использование переменных
В приведенном выше примере, в правиле для 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 = 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-файл включаются также и другие цели помимо компиляции:
1: # Makefile
2:
3: 0BJS = foo.о bar.о baz.o
4: LDLIBS = -L/usr/local/lib/ -lbar
5:
6: foo: $(OBJS)
7: gcc -o foo $ (OBJS) $ (LDLIBS)
8:
9: install: foo
10: install -m 644 foo /usr/bin
11: .PHONY: install
- Строка 1 — это комментарий; make следует обычной традиции Unix определения комментариев с помощью символа #.
- В строке 3 определяется переменная по имени 0BJS как foo.о bar.o baz.о.
- В строке 4 определяется другая переменная — LDLIBS.
- В строке 6 начинается определение правила, которое указывает на то, что файл foo зависит от (в этом случае, собран из) файлов, имена которых содержатся в переменной OBJS. foo называется целевым объектом, а $ (OBJS) — списком зависимостей. Обратите внимание на синтаксис расширения переменной: имя переменной помещается в $(...).
- Строка 7 — это командная строка, указывающая на то, как построить целевой объект из списка зависимостей. Командных строк может быть много, и первым символом в командной строке должна быть табуляция.
- Строка 9 — довольно интересный целевой объект. Фактически тут не
предпринимается попытка создать файл по имени install; вместо этого foo инсталлируется в /usr/bin с помощью стандартной программы install. Эта строка вызывает неоднозначность в make: что, если файл install уже существует и является более новым, нежели foo? В этом случае запуск команды
make install
приведет к выдаче сообщения'install' is up to date
(install новее) и завершению работы. - Строка 11 указывает make на то, что install не является файлом, и что необходимо игнорировать любой файл по имени install при вычислении зависимости install. Таким образом, если зависимость install была вызвана (как это сделать мы рассмотрим далее), команда в строке 10 всегда будет выполняться.
. PHONY — это директива, которая изменяет операцию make; в этом случае она указывает make на то, что целевой объект install не является именем файла.
Целевые объекты . PHONY часто используются для совершения действий вроде инсталляции или создания одиночного имени целевого объекта, которое основывается на нескольких других уже существующих целевых объектов, например:
all: foo barbaz
.PHONY: all
К сожалению, . PHONY не поддерживается некоторыми версиями make. Менее очевидный, не такой эффективный, но более переносимый способ для этого показан ниже.
all: foo bar baz FORCE
FORCE:
Это срабатывает только тогда, когда нет файла по имени FORCE.
Суффиксные правила
Суффиксные правила — это другая область, в которой вам нужно решить, писать ли стандартные make-файлы или использовать расширения GNU. Стандартные суффиксные правила намного ограниченнее, нежели шаблонные правила GNU, но во многих ситуациях стандартные суффиксные правила могут оказаться полезными. К тому же шаблонные правила поддерживаются не во всех версиях make. Суффиксные правила выглядят следующим образом:
.с.о:
$(СС) -с $(CFLAGS) $(CPPFLAGS) -о $@ $<
.SUFFIXES: .с .о
В этом правиле говорится (если не касаться излишних деталей), что make должна, если не было других явных указаний, превратить файл а.с в а.о путем запуска приложенной командной строки. Каждый файл .с будет рассматриваться так, будто он явно внесен в список в качестве зависимости соответствующего файла .о в вашем make-файле.
Это суффиксное правило демонстрирует другую возможность make
— автоматические переменные. Понятно, что нужно найти способ подставить
зависимость и целевой объект в командную строку. Автоматическая
переменная $@ выступает в качестве целевого объекта, $< выступает в качестве первой зависимости, а $^ представляет все зависимости.
Существуют и другие автоматические переменные, которые рассматриваются в руководстве по make.
Все автоматические переменные можно использовать в обыкновенных
правилах, а также в суффиксных и шаблонных правилах. Последняя строка
примера представляет еще одну директиву .SUFFIXES указывает make на то, что .с и .о
являются суффиксами, которые должен использовать make для нахождения
способа превратить существующие исходные файлы в необходимые целевые
объекты.
Шаблонные правила более мощные и, следовательно, немного
сложнее, чем суффиксные правила. Ниже приведен пример эквивалентного
шаблонного правила для показанного выше суффиксного правила.
%.о: %.с
$(СС) -с $(CFLAGS) $(CPPFLAGS) -о $@ $<
Большинство крупных проектов с открытым исходным кодом используют инструменты Automake, Autoconf и Libtool. Эти инструменты представляют собой коллекцию знаний об особенностях различных систем и стандартах сообщества, которая может помочь в построении проектов. Таким образом, потребуется писать лишь немного кода, специфического для проекта. Например, Automake пишет целевые объекты install и uninstall, Autoconf автоматически определяет возможности системы и настраивает программное обеспечение для его соответствия системе, a Libtool отслеживает различия в управлении совместно используемыми библиотеками на разных системах.