make

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 19:38, 13 января 2018.

make
Парадигма макро, декларативное программирование
Спроектировано Стюарт Фельдман
Первый   появившийся April 1976; 45 years ago (1976-04)
Язык физической реализации C
OS UNIX
Формат файлов Makefile
Главная реализация
BSD, GNU, nmake
Диалект
BSD make, GNU make, Microsoft nmake
Влияние
Apache Ant, Rake, MSBuild, и другие

make представляет собой утилиту, автоматизирующую процесс преобразования файлов из одной формы в другую. Чаще всего это компиляция исходного кода в объектные файлы и последующая компоновка в исполняемые файлы или библиотеки. Утилита использует специальные make-файлы, в которых указаны зависимости файлов друг от друга и правила для их удовлетворения. На основе информации о времени последнего изменения каждого файла make определяет и запускает необходимые программы.[Источник 1] Утилита make автоматически определяет какие части большой программы должны быть перекомпилированы, и выполняет необходимые для этого действия.

История

Стюарт Фельдман

В настоящее время существует множество утилит для отслеживания зависимостей, но make является одной из самых распространенных, в первую очередь из-за ее включения в Unix, начиная с версии PWB/UNIX (англ. Programmer’s Workbench), которая содержала инструменты для разработки программного обеспечения. Make была создана Стюартом Фельдманом в 1977 году в Bell Labs. Фельдман получил премию за авторскую разработку этого широко распространенного инструмента.[Источник 2]Makefiles были текстовыми файлами, а не закодированными двоичными файлами, потому что это был идеал Unix: печатный, отлаживаемый, понятный материал. До создания make системы сборки (компиляции) ПО Unix обычно состояли из shell-скриптов сборки, сопровождавших исходный код программ.

Версии

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

  • GNU make — самый распространенный и функциональный вариант
  • BSD make (pmake) — используется в проектах BSD, по функциональности примерно соответствует GNU make
  • nmake (Microsoft make) — работает под Windows, малофункционален, только базовые принципы make.

Основные принципы

Утилита make работает по правилам (rules), записанным в специальном конфигурационном файле. Правила определяют цели (targets), зависимости между целями и набор команд для выполнения каждой цели.Цели могут соответствовать определенным файлам. Кроме того, цели могут не соответствовать ни одному файлу и использоваться для группировки других целей или определенной последовательности команд. Такие цели называются phony targets.Каждая цель может зависеть от выполнения других целей. Выполнение цели требует предварительного выполнения других целей, от которых она зависит. В случае зависимости между целями, соответствующими файлам, цель выполняется только в том случае, если файлы, от которых она зависит, новее, чем файл, соответствующий цели. Это позволяет перегенерировать только файлы, зависящие от измененных файлов, и не выполнять потенциально долгий процесс пересборки всех файлов проекта.Таким образом, makefile определяет граф зависимостей, по которому утилита make выполняет ту или иную цель, по возможности минимизируя количество операций сборки. [Источник 3]

Цели для сборки дистрибутивов GNU

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

  • all — выполнить сборку пакета;
  • install — установить пакет из дистрибутива (производит копирование исполняемых файлов, библиотек и документации в системные каталоги);
  • uninstall — удалить пакет (производит удаление исполняемых файлов и библиотек из системных каталогов);
  • clean — очистить дистрибутив (удалить из дистрибутива объектные и исполняемые файлы, созданные в процессе компиляции);
  • distclean — очистить все созданные при компиляции файлы и все вспомогательные файлы, созданные утилитой ./configure в процессе настройки параметров компиляции дистрибутива.

По умолчанию make использует самую первую цель в make-файле.

Специальные цели

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

  • .PHONY — определяет набор phony targets;
  • .DEFAULT — определяет цель, которая будет вызываться, если не найдена зависимая цель, необходимая для выполнения другой цели;
  • .IGNORE — указывает цели, для которых необходимо игнорировать ошибки при выполнении цели;
  • .SILENT — определяет набор целей, для которых необходимо подавлять вывод команд, выполняющих цель;
  • .NOTPARALLEL — запрещает параллельное выполнение makefile;
  • .ONESHELL — выполняет набор команд цели в одном процессе shell.

Структура make-файлов

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

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

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

Опции

Опции – параметры, определяющие дополнительные условия при выполнении правил. Опции всегда начинаются со знака ‘-‘.[Источник 5]

-n Предписывает выводить команды, порождаемые make'ом, не выполняя их на самом деле
-s Подавляет вывод командный строк перед их выполнением (если входящая в правило команда начинается с @, она не распечатывается)
-S Отменяет действие опции –k. Это необходимо только при рекурсивном запуске make.
-i Предписывает игнорировать ошибки (ошибка игнорируется также, если командная строка в файле описаний начинается со знака минус)
-I dir Определяет директории для поиска включенных makefiles. В случае, если определяется несколько различных путей, поиск будет производиться в порядке их перечисления.
-k При возникновении ошибки выполнение команд, связанных с текущей зависимостью, прекращается, однако обработка других зависимостей продолжается
-f имя_файла Задает имя файла описаний (пример: make -f mymakefile)
-p Указывает make'у вывести все макроопределения, а также описания зависимостей и операций для создания целевых_файлов. Если использовать пустой make-файл, мы получим полную информацию о неявных предопределенных правилах, макросах и суффиксах. Имя файла-обозначает стандартный ввод. Если опция не указана, читается файл с именемmakefileилиMakefile.
-C dir осуществляется переход в директорию dirдо прочтения makefile. В случае многократного применения опции -C , каждая из них относится к предыдущей: -C / -C etc эквивалентно -C /etc. Это часто используется при рекурсивном вызове make.
-v Печать версии make

Пример

Пусть программа program состоит из пары файлов кода — main.c и lib.c, а также из одного заголовочного файла — defines.h, который подключён в обоих файлах кода. Поэтому, для создания program необходимо из пар (main.c defines.h) и (lib.c defines.h) создать объектные файлы main.o и lib.o, а затем скомпоновать их в program. При сборке вручную требуется исполнить следующие команды:

cc -c main.c
cc -c lib.c
cc -o program main.o lib.o

Если в процессе разработки программы в файл defines.h будут внесены изменения, потребуется перекомпиляция обоих файлов и линковка, а если изменим lib.c, то повторную компиляцию main.о можно не выполнять. Следовательно, для каждого файла, который мы должны получить в процессе компиляции, нужно указать, на основе каких файлов и с помощью какой команды он создаётся. Программа make на основе этих данных выполняет следующее:

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

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

program: main.o lib.o
        cc -o program main.o lib.o
main.o lib.o: defines.h

В имени второй цели указаны два файла и для этой же цели не указана команда компиляции. Также, нигде явно не указана зависимость объектных файлов от «*.c»-файлов. Дело в том, что программа make имеет предопределённые правила для получения файлов с определёнными расширениями. Так, для цели-объектного файла (расширение «.o») при обнаружении соответствующего файла с расширением «.c» будет вызван компилятор «сс -с» с указанием в параметрах этого «.c»-файла и всех файлов-зависимостей. Синтаксис для определения переменных: переменная = значение Значением может являться произвольная последовательность символов, включая пробелы и обращения к значениям других переменных. С учётом сказанного, можно модифицировать наш make-файл следующим образом:

OBJ = main.o lib.o
program: $(OBJ)
        cc -o program $(OBJ)
$(OBJ): defines.h

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

foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:
        echo $(foo)

Переменные

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

СС=gcc
IDIR=../include
CFLAGS=-I$(IDIR)
DEPS=hellomake.o hellofunc.p
NAME=hellomake
 
$(NAME): $(DEPS)
  $(CC) -o $(NAME) $(DEPS)

Варианты использования

Чаще всего о make говорят в контексте сборки программ на C/C++, в конце концов, для этого он изначально предназначался. Однако, make ­— гораздо более универсальный инструмент. Записывая makefile, где декларативно описывается определенное состояние отношений между файлами, которое каждый запуск make будет стараться поддерживать. Декларативный характер определения состояния очень удобен, в случае использования какого-либо императивного языка (например, shell) нам приходилось бы выполнять большое количество различных проверок, получая на выходе сложный и запутанный код. Кроме того, использование зависимостей между phony targets, позволяющий, по сути, декларативно описывать некий (ограниченный) конечный автомат, может быть полезно для написания различных сценариев. Используя make в качестве каркаса для выполнения различных shell-команд, мы получаем по сути некий базовый framework для shell с готовым пользовательским интерфейсом (вызов make + передача переменных), встроенными средствами отслеживания зависимостей, параллельного выполнения, макроопределениями и т.д. Поэтому базовое знание make позволяет в ряде случаев решить проблему быстрым и достаточно надежным способом.

Утилита make

Источники

  1. make // Википедия. [2017-2017]. Дата обновления:9.02.2017 URL:https://ru.wikipedia.org/wiki/Make (дата обращения: 28.10.2017).
  2. make // Wikipedia. [2017-2017]. Дата обновления:23.10.2017URL:https://en.wikipedia.org/wiki/Make_(software) (дата обращения: 29.10.2017).
  3. Введение в make [2017-2017] // URL:http://pushorigin.ru/bash/make (дата обращения:29.10.2017).
  4. Структура make-файлов [2017-2017] // URL:http://bikmeyev-at.ugatu.su/students/OSiS/lab06/os_06_02.html (дата обращения:29.10.2017).
  5. Запуск утилиты make [2014-2017] // URL:https://studfiles.net/preview/935666/ (дата обращения:29.10.2017).