Инструментарий программиста в Linux: MAKE

make

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

Makefile

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

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

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

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

ПЕРЕМЕННАЯ = ЗНАЧЕНИЕ

ЦЕЛЬ1 ... : [ЗАВИСИМОСТЬ ...]
    КОМАНДА 1  
    КОМАНДА 2
ЦЕЛЬ2 ... : [ЗАВИСИМОСТЬ ...]
    КОМАНДА 1
    КОМАНДА 2  
...
  • ПЕРЕМЕННАЯ - похожа на переменную в sh. Может использоваться для подстановки значения в целях, зависимостях и командах
  • ЦЕЛЬ - обычно имя файла, который надо создать. Примерами целей являются исполняемые или объектные файлы. Цель также может быть именем выполняемого действия, как, например, clean.
  • ЗАВИСИМОСТЬ - это файл, изменение которого требует персборки цели. Часто цель зависит от нескольких файлов.
  • КОМАНДА - это действие, которое выполняет make, например запуск компилятора или удаление временных файлов. Каждая строка правила начинается с символа табуляции. Правило может иметь более чем одну команду - каждую на своей собственной строке. Длинные строки разбиваются на несколько с использованием обратного слэша, за которым следует перевод строки. Знак диез # является началом комментария. Строка с # до конца игнорируется. Комментарии так же могут переноситься на несколько строк с помощью обратного слэша в конце строки.

Запуск make

Синтаксис:

make [Опции] [Переменная='abc'] [Цель]

Квадратные скобки означают необязательность присутствия данной части.

  • Цель - имя цели, которую надо выполнить. По умолчанию - первая цель, описанная в make-файле.
  • Переменная ='abc' - переопределение переменных. Значения переменных, введенных в командной строке, имеют больший приоритет, чем определения в make-файле.

Опции:

  • -f file - явное задание имени make-файла
  • -n - имитация действий без реального выполнения, служит для отладки
  • -t - изменение времени модификации цели без реального выполнения
  • -q - проверка на необходимость обновления цели без реального выполнения.

Более сложные способы применения MAKE

Для автоматической генерации зависимостей от файлов заголовков в языках C и C++ можно использовать команду gcc -M file.c или gcc -MM file.c. Второй вариант отличается тем, что не генерирует зависимости от системных заголовочных файлов.

Правила написания Makefile

  • В качестве ЦЕЛИ или ЗАВИСИМОСТИ может использоваться список файлов через пробел или шаблон в стиле shell.
  • Команды в правилах начинаются со знака табуляции.
  • Порядок правил кроме первого несущественен.
  • По умолчанию главной целью make является первая цель первого правила в первом make-файле. Цель можно явно задать при запуске make.
  • Цель, начинающаяся с точки, не используется как цель по умолчанию, если она не содержит  один или более символов / т.е. определяет путь к файлу; кроме того, по умолчанию не используются цели, определяющие шаблонные правила.
  • Шаблоны интерпретируются в момент выполнения правила, при присваивании переменным интерпретация шаблона не происходит.

Автоматические переменные

В КОМАНДАХ можно использовать автоматические переменные. Эти переменные заново вычисляются для каждого выполняемого правила на основе цели и зависимостей правила.

Автоматическая переменная Назначение
$@ Имя файла цели правила. В шаблонном правиле с несколькими целями,имя той цели, которая вызвала выполнение команд правила.
$< Имя первой зависимости. Если цель получила свои команды из неявного правила, то это будет первая зависимость, добавленная неявным правилом.
$? Имена всех зависимостей, которые являются более новыми, чем цель, с  пробелами между ними.
$^ Имена всех зависимостей, с пробелами между ними. Если Вы для цели неоднократно укажете одну и ту же зависимость, значение переменной '$^' будет содержать только одну копию ее имени.
$+ Эта переменная аналогична переменной '$^', только зависимости, указанные неоднократно дублируются в том порядке, в котором они указаны в make-файле. Это в первую очередь полезно для использования в командах компоновки, где является существенным повторение имен библиотек в определенном порядке
$* База с которой сопоставляется неявное правило (см. ниже). В шаблонном правиле база представляет собой часть имени файла, которая сопоставляется символу '%' в шаблоне цели. Если целью является файл 'dir/a.foo.b', а   шаблон цели - 'a.%.b', то базой будет 'dir/foo'. База полезна для создания имен файлов, связанных с правилом. В явных правилах база не определена как имя файла без расширения,если такое расширение можно выделить. Не рекомендуется использовать эту переменную в явных правилах

Неявные правила

Неявные правила определены для многих языков программирования и применяются в соответствии с расширением исходного файла. По умолчанию в gnu make список расширений такой : .out, .a, .ln, .o, .c, .cc, .C, cpp, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch, .web, .sh, .elc, .el. При использовании неявных правил используются переменные, переопределяя которые можно управлять процессом преобразования файлов, например, указывать нестандартный компилятор или передавать ему опции.

Исходный файл Порожденный файл Команда
Компиляция C-программ 'file.c' 'file.o' $(CC) -c $(CPPFLAGS) $(CFLAGS) file.c
Компиляция программ на языке C++
'file.cc'
или 'file.C'
'file.o' $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) file .cc
Компиляция программ на Фортране
'file.f'
'file.o' $(FC) -c $(FFLAGS) file .f

Пример MakeFile/Использование действий по умолчанию

# Цель по умолчанию  - исполняемый edit
edit : main.o kbd.o command.o display.o
    cc -o edit main.o kbd.o command.o display.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
clean :
    rm edit main.o kbd.o command.o display.o

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

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

Для сокращения записи можно использовать переменные и действия по умолчанию (неявные правила)

.PHONY : all clean 

all: edit
objects = main.o kbd.o command.o display.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
clean :
    rm edit $(objects)

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

file.c: file.o   $(CC) -c file.c

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

objects = main.o kbd.o command.o display.o

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

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

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