Здесь представлен перевод оригинальной документации по утилите GNU make [1], предназначенной для автоматизации процесса компиляции программного обеспечения. Какие файлы необходимо перекомпилировать, make определяет на основе даты модификации файлов и зависимостей одних частей программы от других. Многие недолюбливают make из-за её запутанного синтаксиса makefile, и уже существуют и другие системы автоматизации сборки, возможно более удобные и понятные, и с более продвинутыми возможностями, такие как Zephyr/CMake [2]. Также многие современные языки программирования (такие как Rust [3]) содержат встроенные системы сборки. Однако традиционно make все еще остается актуальной и на сегодняшний день.
Примечание: это обширное руководство может показаться довольно сложным и запутанным. Увы, ИМХО это на самом деле так, поскольку make за многие годы обросла громоздким функционалом, иногда сложным для понимания. Однако существуют и более простые, практичные руководства, позволяющие быстро начать использовать make, например [7].
[Сокращенное оглавление]
1. Обзор make. 2. Введение в файлы Makefile. 3. Написание файлов Makefile. 4. Написание правил. 5. Написание списков рецептов (recipe) в правилах. 6. Как использовать переменные в makefile. 7. Операции проверки условий в файлах Makefile. 8. Функции для преобразования текста. 9. Как запускать make. 10. Использование неявных правил. 11. Использование make для обновления файлов архива (библиотеки). 12. Расширение GNU make. 13. Интеграция GNU make. 14. Функциональные возможности GNU make. 15. Несовместимые и отсутствующие фичи. 16. Соглашения Makefile.
Приложение A. Краткий справочник. Приложение B. Ошибки, генерируемые Make. Приложение C. Пример сложного Makefile. Appendix D GNU Free Documentation License
[Полный список глав]
1. Обзор make. 1.1. Как следует читать это руководство. 1.2. Problems and Bugs.
2. Введение в файлы Makefile. 2.1. Как выглядит правило (rule) в makefile. 2.2. Простой Makefile. 2.3. Как make обрабатывает Makefile. 2.4. Переменные make упрощают файлы Makefile. 2.5. Дадим make возможность самой формировать рецепт. 2.6. Другой стиль для Makefile. 2.7. Правила для очистки директории.
3. Написание файлов Makefile. 3.1. Что содержат файлы Makefile. 3.1.1. Разбиение длинных строк. 3.2. Какое имя дать вашему Makefile. 3.3. Подключение других файлов Makefile. 3.4. Переменная MAKEFILES. 3.5. Как заново создаются файлы Makefile. 3.6. Переопределение части другого Makefile. 3.7. Как make читает Makefile. 3.8. Как парсятся файлы Makefile. 3.9. Вторичное расширение (Secondary Expansion).
4. Написание правил. 4.1. Пример правила. 4.2. Синтаксис правила. 4.3. Типы prerequisites. 4.4. Использование Wildcard в именах файлов. 4.4.1. Примеры wildcard. 4.4.2. Распространенные ошибки в использовании wildcard. 4.4.3. Функция wildcard. 4.5. Директории поиска для prerequisites. 4.5.1. VPATH: путь поиска для всех prerequisites. 4.5.2. Директива vpath. 4.5.3. Как происходит поиск по директориям. 4.5.4. Написание рецептов с использованием Directory Search. 4.5.5. Поиск по директориям и неявные правила. 4.5.6. Поиск по директориям для линковки библиотек. 4.6. Фальшивые цели (Phony Targets). 4.7. Правила без рецептов или prerequisites. 4.8. Пустые target-файлы для записи событий. 4.9. Специальные встроенные имена целей. 4.10. Несколько целей в правиле. 4.11. Несколько правил для одного target-файла. 4.12. Правила статического шаблона. 4.12.1. Синтаксис правил статического шаблона. 4.12.2. Отличие правил статического шаблона от неявных правил. 4.13. Правила двойного двоеточия. 4.14. Автоматическая генерация prerequisites.
5. Написание списков рецептов (recipe) в правилах. 5.1. Синтаксис рецепта. 5.1.1. Разделение на части строк рецепта. 5.1.2. Использование переменных в рецепте. 5.2. Эхо в рецепте. 5.3. Выполнение рецепта. 5.3.1. Использование одного шелла. 5.3.2. Выбор шелла. 5.4. Параллельное выполнение. 5.4.1. Отключение параллельного выполнения. 5.4.2. Вывод при параллельном выполнении. 5.4.3. Ввод при параллельном выполнении. 5.5. Ошибки в списках команд рецепта. 5.6. Прерывание, или аварийное завершение работы make. 5.7. Рекурсивное использование make. 5.7.1. Как работает переменная MAKE. 5.7.2. Передача переменных в sub-make. 5.7.3. Передача опций в sub-make. 5.7.4. Опция '--print-directory'. 5.8. Определение фиксированных (canned) рецептов. 5.9. Использование пустых рецептов.
6. Как использовать переменные в makefile. 6.1. Ссылки на переменные: общая информация. 6.2. Две разновидности (flavor) переменных. 6.2.1. Рекурсивно расширяемое присваивание переменной. 6.2.2. Просто расширяемое присваивание переменной. 6.2.3. Немедленно расширяемое присваивание переменной. 6.2.4. Условное присваивание переменной. 6.3. Продвинутые возможности для ссылок на переменные. 6.3.1. Ссылка с подстановкой. 6.3.2. Вычисленные имена переменных. 6.4. Как переменные получают свои значения. 6.5. Установка переменных. 6.6. Добавление текста к переменным. 6.7. Директива override. 6.8. Определение многострочных переменных. 6.9. Отмена определений переменной. 6.10. Переменные из окружения. 6.11. Значения переменных, специфичных для target. 6.12. Значения переменных, специфичных для шаблона. 6.13. Подавление наследования переменных. 6.14. Другие специальные переменные.
7. Операции проверки условий в файлах Makefile. 7.1. Пример проверки условия. 7.2. Синтаксис проверок условия. 7.3. Проверка флагов.
8. Функции для преобразования текста. 8.1. Синтаксис вызова функции. 8.2. Функции для подстановки и анализа строк. 8.3. Функции для имен файлов. 8.4. Функции для проверки условий. 8.5. Функция let. 8.6. Функция foreach. 8.7. Функция file. 8.8. Функция call. 8.9. Функция value. 8.10. Функция eval. 8.11. Функция origin. 8.12. Функция flavor. 8.13. Функции, управляющие поведением make. 8.14. Функция shell. 8.15. Функция guile.
9. Как запускать make. 9.1. Аргументы, указывающие файл Makefile. 9.2. Аргументы, указывающие goals. 9.3. Вместо выполнения recipes. 9.4. Как избежать перекомпиляции некоторых файлов. 9.5. Переназначение переменных. 9.6. Тестирование компиляции программы. 9.7. Временные файлы. 9.8. Сводка по опциям make.
10. Использование неявных правил. 10.1. Основы использования неявных правил. 10.2. Каталог встроенных правил. 10.3. Переменные, используемые неявными правилами. 10.4. Цепочки неявных правил. 10.5. Определение и переопределение правил шаблона. 10.5.1. Введение в правила шаблона. 10.5.2. Примеры правил шаблона. 10.5.3. Автоматические переменные make. 10.5.4. Как работает совпадение шаблонов. 10.5.5. Правила шаблона любого совпадения. 10.5.6. Отмена неявных правил. 10.6. Определение правил по умолчанию для последней инстанции. 10.7. Старомодные правила суффиксов. 10.8. Алгоритм поиска неявного правила.
11. Использование make для обновления файлов архива (библиотеки). 11.1. Элементы архива в качестве целевых объектов. 11.2. Неявное правило для целевых объектов элемента архива. 11.2.1. Обновление директорий символов архива. 11.3. Риски при использовании архивов. 11.4. Правила суффикса для файлов архива.
12. Расширение GNU make. 12.1. Интеграция GNU Guile. 12.1.1. Преобразование типов Guile. 12.1.2. Интерфейсы из Guile для make. 12.1.3. Пример использования Guile в make. 12.2. Загрузка динамических объектов. 12.2.1. Директива load. 12.2.2. Как пересоздаются загружаемые объекты. 12.2.3. Интерфейс загружаемого объекта. 12.2.4. Пример загружаемого объекта.
13. Интеграция GNU make. 13.1. Совместное использование слотов заданий с GNU make. 13.1.1. Интеграция POSIX Jobserver. 13.1.2. Интеграция Windows Jobserver. 13.2. Синхронизированный вывод в терминал.
14. Функциональные возможности GNU make.
15. Несовместимые и отсутствующие фичи.
16. Соглашения Makefile. 16.1. Общие соглашения для файлов Makefile. 16.2. Утилиты в файлах Makefile. 16.3. Переменные для указания команд. 16.4. DESTDIR: поддержка поэтапных инсталляций. 16.5. Переменные для директорий инсталляции. 16.6. Стандартные целевые объекты для пользователей. 16.7. Категории команд установки.
Приложение A. Краткий справочник. Приложение B. Ошибки, генерируемые Make. Приложение C. Пример сложного Makefile. Appendix D GNU Free Documentation License
[1. Обзор make]
Утилита make автоматически определяет, какие части большой программы должны быть перекомпилированы, и выдает для этого соответствующие команды. В этом руководстве описываются принципы работы make, изначально реализованной Ричардом Столманом (Richard Stallman) и Роландом МакГрафом (Roland McGrath). Начиная с версии 3.76 разработку продолжил Пол Смит (Paul D. Smith).
GNU make соответствует секции 6.2 стандарта IEEE 1003.2-1992 (POSIX.2).
В примерах с make будут использованы куски кода программ на языке C, поскольку это самый распространенный язык программирования, компилятор которого может запущен командой shell (в большинстве систем Linux этот компилятор установлен по умолчанию). Конечно, это не ограничивает области применения утилиты make. Вы можете использовать её для описания любой задачи, где какие-либо файлы должны быть автоматически обновлены всякий раз, когда изменяются другие файлы.
1.1. Как следует читать это руководство
Если вы новичок с make, или ищете материалы для основного знакомства, то прочитайте для начала несколько секций каждой главы, пропуская последующие секции. В каждой главе первые несколько секций содержат введение или основную информацию, и в последующих секциях присутствует более подробная специализированная или техническая информация. Исключение составляет вторая глава, "Введение в файлы Makefile", все содержимое которой это базовая вводная информация.
Если вы знакомы с другими программами make, то см. 14. Функциональные возможности GNU make, перечислены улучшения GNU make, а также 15. Несовместимые и отсутствующие фичи, где описано то немногое, чего недостает GNU make, и что имеется в её альтернативах.
Для быстрого обзора просмотрите врезку "Сводка по опциям" [4], Приложение A. Краткий справочник и описание встроенных имен целей [5].
Примечание: некоторые специфические термины для упрощения оставлены без перевода, для их расшифровки см. "Словарик Makefile" в конце статьи.
Подготовка и запуск make. Для того, чтобы использовать make, необходимо написать для неё специальный файл команд - makefile, который описывает взаимоотношения между файлами в вашей программе, и предоставляет команды для обновления каждого файла. Обычно целевая разрабатываемая вами программа это исполняемый файл, который обновляется (процессом линковки) в зависимости от обновления файлов объектного кода, которые в свою очередь должны быть обновлены при изменении исходного кода программы, что происходит при компиляции.
Как только подходящий makefile создан и существует в корневом каталоге проекта, для запуска процесса компиляции достаточно сделать текущим (командой cd) рабочий каталог проекта программы, и выполнить команду:
$ make
Утилита make использует базу данных в файле makefile и метки времени последней модификации файлов проекта, чтобы определить, какие файлы должны быть обновлены. Для каждого из этих файлов make запускает блоки команд рецептов, записанные в makefile.
Вы можете предоставить make аргументы командной строки, чтобы управлять тем, какие файлы должны быть перекомпилированы, или каким образом [4]. Полезные советы и хорошие практические примеры см. в статье [7].
[2. Введение в файлы Makefile]
Итак, вам нужен makefile, чтобы указать make, что и как делать. Чаще всего эти указания заключаются в описании, какие файлы исходного кода компилировать, и какие передать в процесс линковки, чтобы получилась исполняемая программа.
В этой главе мы обсудим простой makefile, который описывает как скомпилировать и выполнить линковку текстового редактора, состоящего из восьми файлов исходного кода языка C и трех заголовочных файлов. Этот makefile может также указать make, как запускать различные команды, когда они явно запрашиваются (например, чтобы удалить определенные файлы при операции очистки проекта). Чтобы ознакомиться с более сложным примером makefile, см. Приложение C. Пример сложного Makefile.
Когда перекомпилируется программа редактора текста, необходимо перекомпилировать каждый файл его исходного кода C. Если поменялся заголовочный файл, то должен быть перекомпилирован каждый файл исходного кода C, который подключает этот заголовочный файл. Каждая компиляция генерирует объектный файл для каждого файла исходного кода. И наконец, если был перекомпилирован любой из файлов исходного кода, то все объектные файлы программы должны быть заново линкованы друг с другом, чтобы получился актуальный исполняемый файл программы текстового редактора.
2.1. Как выглядит правило (rule) в makefile
Простой makefile состоит из "правил" (rules) следующего вида:
target ... : prerequisites ...
строка рецепта
...
...
Здесь target (цель) это обычно имя файла, который генерируется программой (компилятором или линковщиком); чаще всего примерами target служат исполняемые или объектные файлы. Также target может быть именем специально выполняемого действия, такого как clean (см. 4.6. Фальшивые цели (Phony Targets), а также [8]).
Prerequisite это файл, который используется как входные данные для создания target. Target часто зависит от нескольких файлов.
Рецепт (recipe) это действие, которое выполняет make. Рецепт может содержать одну или большее количество команд shell, записанных в makefile каждая в отдельной строке, причем каждая такая строка должна содержать в начале символ табуляции ('\t', ASCII 9, 0x09 HEX). Обратите на это внимание: часто это требование вводит новичков в ступор. Если вы по какой-то причине предпочли бы использовать для этой цели другой символ, не символ табуляции, то можете установить переменную окружения .RECIPEPREFIX (см. 6.14. Другие специальные переменные).
Обычно рецепт находится с правилом, содержащим файлы prerequisite, и рецепт обслуживает создание файл target правила, если поменялся один из файлов prerequisite. Однако правило, которое указывает рецепт для собираемой цели (target), не обязательно имеет prerequisites. Например правило, содержащее команду удаления, связанную с target 'clean', не имеет prerequisites.
Правило (rule) в свою очередь объясняет make, как и когда создавать заново определенные файлы, являющиеся целевыми (targets) определенного правила. Утилита make выполняет рецепт на его файлах prerequisites, чтобы создать или обновить target. Правило может также объяснять, как и когда выполнять свое действие. См. 4. Написание правил.
Файл makefile может содержать другой текст помимо правил, однако простой makefile должен содержать только правила. Правила в реальной жизни могут выглядеть несколько сложнее, чем в примере шаблона, показанного в следующей секции, но все же они так или иначе ему все равно соответствуют.
2.2. Простой Makefile
Ниже показан прямолинейный makefile, описывающий, что исполняемый файл с именем edit зависит от восьми объектных файлов (*.o), которые в свою очередь зависят от восьми исходных модулей C (*.c) и трех заголовочных файлов (*.h).
В этом примере все файлы исходного кода C подключают 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
Обратите внимание, что длинные строки команд для удобства и читаемости поделены на две строки с помощью символов обратного слеша (backslash \) и символа новой строки (0x0A); см. 3.1.1. Разбиение длинных строк.
Чтобы использовать этот makefile для генерации исполняемого файла с именем edit, выполните команду в каталоге, где находятся файлы исходного кода, заголовки и сам makefile (это называют рабочим каталогом проекта):
$ make
Чтобы использовать этот makefile для удаления исполняемого файла и всех объектных файлов из рабочей директории, выполните команду:
$ make clean
В этом примере makefile цели (targets) включают исполняемый файл edit, и объектные файлы main.o и kbd.o. Prerequisites это такие файлы, как main.c и defs.h. Фактически, каждый файл '*.o' является как target, так и prerequisite. Рецепты включают команды 'cc -c main.c' и 'cc -c kbd.c'.
Когда target это файл, он должен быть перекомпилирован или заново линкован, если изменились его prerequisites. Кроме того, сначала должны быть обновлены любые автоматически сгенерированные prerequisites. В этом примере edit зависит от каждого из восьми объектных файлов; объектный файл main.o зависит от файла исходного кода main.c и заголовочного файла defs.h.
Рецепт может находиться за каждой строкой, где находится target и её prerequisites. Эти строки рецепта говорят, как обновлять файл target. Каждая строка команды в рецепте обязательно должна начинаться на символ табуляции (либо другой символ, если он указан через переменную окружения .RECIPEPREFIX, см. 6.14. Другие специальные переменные); таким способом в makefile отделяются строки рецепта от других строк.
Примечание: фактически наличие требования символа табуляции в начале строк рецепта означает два совершенно разных синтаксиса строк для makefile. Подробнее об этом см. [6].
Имейте в виду, что make ничего не знает о том, как работают строки рецепта. В вашей зоне ответственности правильно создать рецепты, которые будут правильно обновлять файл target. Утилита make всего лишь выполняет строки рецепта, и это все, что нужно выполнить для обновления файла target.
Target 'clean' это не файл, а скорее имя действия. 'clean' не является prerequisite для любого другого правила. Следовательно, make никогда сама не будет выполнять действия для clean, если вы не укажете это специально через командную строку. Обратите внимание, что это правило не только не является prerequisite, но и не имеет для себя никаких prerequisites, поэтому единственным назначением такого правила это запуск указанного рецепта. Target-ы, которые не относятся к файлам, однако являющиеся просто действиями, называются фальшивыми целями (phony targets). Подробнее про такие виды целей см. 4.6. Фальшивые цели (Phony Targets). Чтобы увидеть, как заставить make игнорировать ошибки из rm или любой другой команды, см. [9].
2.3. Как make обрабатывает Makefile
По умолчанию make начинает с первой target (но не тех target, которые начинаются на '.', за исключением ситуации, когда они также содержат один или большее количество символов '/'). Первая target называется целью по умолчанию, default goal, см. также "Словарик Makefile" в конце статьи.
В простом показанном выше примере цель по умолчанию состоит в обновлении исполняемого файла программы edit; поэтому его правило указано первым.
Таким образом, когда вы запускаете команду:
$ make
... утилита make читает файл makefile в текущей директории, и начинает обработку в нем первого правила. В этом примере таким правилом является повторная линковка edit; однако перед тем, как утилита make сможет полностью обработать это правило, она должна обработать правила для файлов, от которых зависит edit, в нашем случае это объектные файлы. Каждый из этих объектных файлов обрабатывается в соответствии со своим собственным правилом. Эти правила говорят обновить каждый файл *.o путем компиляции файла исходного кода *.c. Перекомпиляция должна выполняться, если либо файл исходного кода, либо любой из заголовочных файлов, перечисленных в его prerequisites, оказался более свежий, чем соответствующий объектный файл, либо этот объектный файл отсутствует.
Другие правила обрабатываются, потому что их target-ы появляются как prerequisites для goal. Если какое-либо другое правило не зависит от goal (или от чего-нибудь, от чего оно зависит, и т. п.), то это правило не обрабатывается, если только вы не указали это специально (командой наподобие make clean).
Перед перекомпиляцией объектного файла make рассматривает обновление его prerequisites, файла исходного кода и файлов заголовка. Этот makefile не указывает ничего, что должно быть для них сделано - файлы *.c и *.h не являются target-ами каких-либо правил - так что make ничего не делает для этих файлов. Однако в то же время make будет обновлять автоматически генерируемые программы C, такие как создаваемые системам Bison или Yacc, по их собственным правилам.
После перекомпиляции в зависимости от того, какой объектный файл в этом нуждается, make принимает решение, следует ли заново линковать edit. Это должно быть сделано, если либо файл edit не существует, либо если любой из объектных файлов более свежий, чем edit. Если был перекомпилирован какой-то объектный файл, то он окажется более свежим, чем файл edit, и тогда будет заново выполнена линковка edit.
Таким образом, если мы поменяли файл insert.c и запустим make, то make скомпилирует этот файл, чтобы обновить insert.o, и затем выполнит линковку edit. Если мы поменяем файл command.h и запустим make, то она перекомпилирует объектные файлы kbd.o, command.o и files.o, и затем выполнит заново линковку файла edit.
2.4. Переменные make упрощают файлы Makefile
В нашем примере мы должны перечислить дважды все объектные файлы:
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
Такое дублирование часто служит источником ошибок; если в систему добавляется новый объектный файл, то хорошо было бы иметь возможность добавлять его только в одном месте, чтобы случайно не забыть про другие. Мы можем устранить риск ошибки и одновременно упростить makefile с помощью переменной. Переменные позволяют определить строку текста только один раз, и затем подставлять её в других местах по имени переменной (см. 6. Как использовать переменные в makefile).
Это стандартная практика для каждого makefile - чтобы в нем была переменная objects, OBJECTS, objs, OBJS, obj, или OBJ, которая содержит список имен всех объектных файлов. Мы определили такую переменную objects следующей строкой в makefile:
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)
2.5. Дадим make возможность самой формировать рецепт
Необязательно расшифровывать для make каждый рецепт, который она должна выполнить для компиляции отдельных файлов исходного кода C, потому что make сама может в этом разобраться: существует так называемое неявное правило (implicit rule) для обновления файла *.o из соответствующего по имени файла *.c, используя команду 'cc -c'. Например, оно будет использовать рецепт 'cc -c main.c -o main.o' для компиляции main.c в объектный файл main.o. Таким образом, мы можем опустить рецепт из правил для объектных файлов. См. 10. Использование неявных правил.
Когда таким способом файл *.c используется автоматически, он также автоматические добавляется в список prerequisites. Так что мы можем опустить файлы *.c из prerequisites, если опустим рецепт.
Ниже показан пример целиком для такой оптимизации, где опущены рецепты и используется переменная 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)
Таким способом мы бы написали свой makefile в реальной жизни (есть еще более лаконичные и удобные способы написания makefile, см. [7]).
Примечание: небольшое усложнение, связанное с .PHONY, относящееся к 'clean', мы рассмотрим позднее. См. "Phony Targets", а также [9]).
Из-за того, что неявные правила удобны, мы часто будем их использовать.
2.6. Другой стиль для Makefile
Когда объекты makefile создаются неявными правилами, можно использовать альтернативный стиль написания makefile. В этом стиле вы группируете элементы по их prerequisites вместо их target-ов. Это может выглядеть примерно так:
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 указывается как prerequisite для всех объектных файлов; command.h и buffer.h это prerequisites перечисленных для них объектных файлов.
Лучше это для вас или нет - дело личного вкуса. Хотя такой стиль makefile делает его компактнее, многим программистам он не нравится, потому что понятнее явным образом поместить всю информацию по каждой target в одном месте.
2.7. Правила для очистки директории
Компилирование программы не единственная цель, для которой мы пишем правила. Файлы makefile часто говорят, как делать и другие вещи помимо компиляции программы: например, как удалить все промежуточные файлы и исполняемый файл, чтобы директория была чистая (clean), подготовленная например архивации проекта или для выкладывания в репозиторий git.
Вот как мы могли бы написать правило make для очистки нашего примера программы редактора текста:
clean:
rm edit $(objects)
На практике мы могли бы захотеть написать правило несколько более сложным образом, чтобы оно справилось с непредвиденными ситуациями. Например, утилита rm может столкнуться с ошибкой, когда не существует один (или несколько) файлов из списка удаления. Мы бы это сделали следующим образом:
.PHONY : clean
clean :
-rm edit $(objects)
Определение .PHONY : clean не дает утилите make перепутать цель clean с именем файла clean (см. 4.6. Фальшивые цели (Phony Targets)). Дефис перед командой rm позволяет игнорировать её ошибку, когда удаляемый файл не существует (см. [9]).
Такое правило, как это, не должно находиться в начале makefile, потому что мы не хотели бы запускать его по умолчанию! Т. е. в нашем примере makefile мы хотим оставить в качестве цели по умолчанию (default goal) правило, которое перекомпилирует исполняемый файл edit редактора текста.
Поскольку clean не является prerequisite для edit, это правило не запустится, когда мы укажем команду 'make' без аргументов. Чтобы указать для make запустить цель clean, мы должны указать её в виде аргумента командной строки: 'make clean'. См. [4].
[3. Написание файлов Makefile]
Информация, которая говорит о том, как перекомпилировать систему, поступает из чтения базы данных, называемой makefile.
3.1. Что содержат файлы Makefile
Файлы makefile содержат в себе пять видов сущностей: явные правила (explicit rules), неявные правила (implicit rules), определения переменных, директивы и комментарии. Правила, переменные и директивы подробно описаны в последующих главах.
• Явное правило говорит, когда и каким образом заново создать один или большее количество файлов, которые называются целями правила (target of rule). Явное правило перечисляет другие файлы, от которых зависят цели, и это перечисление зависимостей называется предварительными требованиями цели (prerequisites of target), и может также задавать рецепт, используемый для создания или обновления цели (целей). См. 4. Написание правил.
• Неявное правило говорит, когда и как заново создать класс файлов, основываясь на их именах (по расширению файла). Он описывает, как цель может зависеть от файла с именем, похожим на цель, и дает рецепт для создания или обновления такой цели. См. 10. Использование неявных правил.
• Определение переменной это строка, которая задает строку текста для переменной. Эта переменная может быть впоследствии подставлена во все нужные места. Пример простого makefile, показанный выше, демонстрирует создание переменной objects для списка объектных файлов компилируемой программы (см. выше секцию "2.4. Переменные make упрощают файлы Makefile").
• Директива эта инструкция для make, чтобы сделать что-либо специально при чтении makefile. Это включает:
- Чтение другого makefile (см. 3.3. Подключение других файлов Makefile). - Принятие решения (основываясь на значениях переменных), использовать или игнорировать часть makefile (так называемые ветвления, см. 7. Операции проверки условий в файлах Makefile). - Определение переменной из verbatim-строки, содержащей несколько строк (см. 6.8. Определение многострочных переменных).
• Символ # в строке файла makefile обозначает начало комментария. Тогда этот символ и все после него в строке игнорируется, за исключением того, что последний backslash, не экранированный другим backslash, продолжит комментарий на нескольких строках. Строка, содержащая только комментарий (возможно с пробелами перед ней), фактически пустая, и игнорируется. Если вы хотите в строке использовать символ # не как комментарий, то экранируйте его с помощью backslash (например \#). Комментарии могут появляться в любой строке makefile, хотя в определенных ситуациях они обрабатываются специальным образом.
Вы не можете использовать комментарии в ссылках на переменные или вызовах функции: внутри ссылки на переменную или вызова функции любой экземпляр # будет обрабатываться литерально (вместо того, чтобы начинать комментарий).
Комментарии внутри рецепта передаются в shell, как и любой другой текст рецепта. И уже shell решает, как это интерпретировать: будет ли это комментарий, зависит от шелла.
В директиве define комментарии не игнорируются во время определения переменной, а остаются неизменными в значении переменной. Когда эта переменная расширяется, она будет обработана либо как комментарий make, либо как текст рецепта, в зависимости от контекста, в котором вычислена переменная.
3.1.1. Разбиение длинных строк. Файлы makefile используют основанный на строках синтаксис, в котором символ новой строки (newline, код ASCII 0x0A) играет специальную роль, помечая конец оператора. У утилиты GNU make нет ограничения на длину строки оператора, она может быть настолько большой, насколько позволит память вашего компьютера.
Однако длинные строки слишком трудно читать без их перескока на новую строк или прокрутки. Так что вы можете отформатировать ваши makefile для удобства путем вставки посередине строки символа newline с помощью символа экранирования в виде обратного слеша (backslash, '\'). Там, где нам нужно будет обозначить различие между строкой оператора и строками, составляющими оператор, мы будем использовать термины "физическая строка" для текста, заканчивающегося символом newline (независимо от того, экранирован ли этот символ) и "логическая строка" которая представляет собой оператор полностью, включая все экранированные символы newline, до самого последнего не экранированного символа newline.
Способ, которым обрабатываются комбинации backslash/newline, зависит от того, относится ли оператор к строке рецепта, или нет. Обработка backslash/newline в строке рецепта будет рассмотрена позже (см. 5.1.1. Разделение на части строк рецепта).
Вне строк рецепта комбинации backslash/newlines преобразуются в один символ пробела. Как только это выполнено, все пробелы вокруг backslash/newline заменяются на одиночный пробел: это включает все пробелы, которые идут перед backslash, все пробелы в начале строки после backslash/newline, и любые последующие комбинации backslash/newline.
Если определена специальная цель .POSIX, то обработка backslash/newline несколько меняется для соответствия POSIX.2: во-перых, пробелы перед backslash не удаляются, и во-вторых, последовательные backslash/newlines не сливаются.
Разделение без добавления пробела. Если вам нужно разделить строку, но вы не хотите, чтобы при этом добавлялся пробел, то вы можете задействовать тонкий трюк: замените ваши пары backslash/newline на три символа: доллар, backslash и newline:
var := one$\
word
После этого make удалит backslash/newline и преобразует их в один пробел, что будет эквивалентно следующему:
var := one$ word
Затем make выполнит расширение переменной. Ссылка на переменную '$ ' означает переменную с одним именем из пробела " ", которая не существует, и как следствие конечный оператор присваивания окажется таким:
var := oneword
3.2. Какое имя дать вашему Makefile
По умолчанию, когда make ищет makefile, она пытается обнаружить и прочитать файлы в следующем порядке: GNUmakefile, makefile и Makefile.
Обычно вы должны назвать свой файл makefile либо makefile, либо Makefile (разработчики make рекомендуют Makefile, потому что он тогда появится на видном месте в начале списка каталогов, рядом с другими важными файлами, такими как README). Первое проверяемое имя GNUmakefile не рекомендуется использовать для большинства файлов makefile. Вам следует использовать это имя, если у вас файл makefile, который специфичен для GNU make, и он не может быть обработан другими версиями make. Другие программы make ищут makefile и Makefile, но не GNUmakefile.
Если утилита make не нашла файл с одним их таких имен, то она не использует никакой makefile. Затем вы должны должны указать goal с аргументом команды, и make попытается выяснить, как её пересоздать, используя только её встроенные неявные правила. См. 10. Использование неявных правил.
Если вы хотите использовать нестандартное имя для своего makefile, то можете указать имя makefile опцией -f или --file. Аргументы -f name или --file=name указывают make прочитать файл с именем name и обработать его как makefile. Если вы используете больше одной опции -f или --file, то можете указать несколько файлов makefile. Все они фактически будут объединены друг с другом в один makefile, соединяясь в том порядке, как они были указаны в командной строке. Имена makefile по умолчанию GNUmakefile, makefile и Makefile не проверяются автоматически, если вы указали опцию -f или --file.
3.3. Подключение других файлов Makefile
Директива include говорит make приостановить чтение текущего makefile, и прочитать один или несколько других файлов makefile перед продолжением чтения текущего. Строка такой директивы выглядит в makefile следующим образом:
include filenames ...
Здесь filenames может содержать имя файлов, распознаваемое шеллом системы. Если filenames пустая строка, то ничего не произойдет и будет выведено сообщение об ошибке.
Разрешено использовать дополнительные пробелы в начале этой строки, однако первый символ в строке не должен быть символом табуляции (или символом, значение котoрого определено переменной .RECIPEPREFIX) - если строка начинается на символ табуляции, то он будет рассматриваться как строка рецепта. Пробел требуется между include и filenames, а также между несколькими именами файлов; лишние пробелы игнорируются внутри и в конце директивы include. Разрешается комментарий, начинающийся с '#', в конце строки. Если имена файлов содержат любые ссылки на переменные или функции, то они будут расширены. См. 6. Как использовать переменные в makefile.
Например, если у вас есть три файла *.mk, a.mk, b.mk и c.mk, и $(bar) расширяется в bish bash, то следующее выражение:
include foo *.mk $(bar)
... эквивалентно foo a.mk b.mk c.mk bish bash.
Когда make обрабатывает директиву include, она приостанавливает чтение содержащего директиву makefile, и читает строки из каждого перечисленного файла по очереди. Когда это завершено, make возобновит чтение makefile, в котором появилась директива include.
Один из случаев использования директив include - когда компилируется несколько программ, каждая из которых обрабатывается своим собственным makefile, каждый в своей директории, когда нужен общий набор определений переменной (см. 6.5. Установка переменных) или правил шаблона (см. 10.5. Определение и переопределение правил шаблона).
Другой такой случай - когда вы хотите автоматически генерировать prerequisites из исходных файлов; эти prerequisites могут быть помещены в файл, включенный в основной makefile. Эта практика как правило чище, чем каким-то образом добавлять prerequisites в конец основного makefile, как это делалось с другими версиями make. См. 4.14. Автоматическая генерация prerequisites.
Если указанное имя не начинается со слеша / (или с буквы диска и двоеточия, когда GNU Make компилирует с поддержкой путей MS-DOS / MS-Windows), и файл не найден в текущей директории, то будут просмотрены несколько других директорий. Сначала будут просмотрены любые директории, которые вы указали опцией -I или --include-dir (см. 9.8. Сводка по опциям make). Затем будут просмотрены следующие директории (если они существуют), в следующем порядке: prefix/include (обычно /usr/local/include(1)) /usr/gnu/include, /usr/local/include, /usr/include.
Примечание (1): компиляция GNU Make для MS-DOS и MS-Windows ведет себя как если бы был определен префикс для корневой директории иерархии дерева DJGPP.
Переменная .INCLUDE_DIRS будет содержать текущий список директорий, которые make будет просматривать при поиске подключаемых файлов. См. 6.14. Другие специальные переменные.
Вы можете избежать поиска в этих директориях по умолчанию путем добавления опции командной строки -I со специальным значением - (например -I-). Это приведет к тому, что make забудет предварительно установленные директории include, включая директории по умолчанию.
Если подключенный makefile не может быть найден в любой из этих директорий, то это не приведет немедленно к фатальной ошибке; обработка makefile, содержащего include, продолжится. Как только make завершит чтение файлов makefile, она попытается пересобрать любые цели, которые устарели или не существуют. См. 3.5. Как заново создаются файлы Makefile. Только после того, как make не сможет найти правило для пересоздания (remake) makefile, или не будет найдено правило, однако рецепт потерпел неудачу, будет диагностирована фатальная ошибка.
Если вы хотите, чтобы make просто игнорировала makefile, который не существует или не может быть заново создан (remade), без сообщения об ошибке, используйте директиву -include вместо include, примерно так:
-include filenames ...
Это действует как include во всех отношениях, за исключением того, что ошибки не произойдет (не будет даже предупреждения) если любой из filenames (или какие-либо из prerequisites любого из filenames) не существует или не может быть заново создан.
Для совместимости с некоторыми другими реализациями make, имеется sinclude как другое имя для -include.
3.4. Переменная MAKEFILES
Если определена переменная окружения MAKEFILES, то make считает её значение списком имен (разделенных пробелом) дополнительных файлов makefile, которые должны быть прочитаны перед другими. Это работает почти как директива include: различные директории просматриваются для этих файловs (см. 3.3. Подключение других файлов Makefile). Дополнительно default goal никогда не будет взята из одного из этих файлов makefile (или любого makefile, который они подключили), и не будет ошибки, если файлы, перечисленные в MAKEFILES, не были найдены.
Основное использование MAKEFILES состоит в коммуникации между рекурсивными вызовами make (см. 5.7. Рекурсивное использование make). Обычно нежелательно устанавливать эту переменную перед вызовом make на верхнем уровне, потому что обычно лучше не связываться с внешними makefile. Однако, если вы запустите make без определенного makefile, то makefile в MAKEFILES может быть полезной вещью, чтобы помочь лучше работать встроенным неявным правилам, таким как определение путей поиска (см. 4.5. Директории поиска для prerequisites).
Некоторые пользователи склонны автоматически устанавливать MAKEFILES в окружении при логине, и программа файлов makefile ожидает, что это будет сделано. Это очень плохая идея, потому что такие файлы makefile будут неправильно работать, если запустить что-то другое. Намного лучше явно подключать директории в файлах makefile. См. 3.3. Подключение других файлов Makefile.
3.5. Как заново создаются файлы Makefile
Иногда файлы makefile могут быть созданы из других файлов, таких как файлов RCS или SCCS. Если makefile может быть заново создан из других файлов, то вы вероятно захотите получить актуальную версию makefile для чтения make.
Для этой цели после чтения всех файлов makefile утилита make будет рассматривать каждый из них как goal target в том порядке, каком они были обработаны, и пытаться их обновить. Если разрешены параллельные сборки (см. 5.4. Параллельное выполнение), то файлы makefile будут также пересобираться параллельно.
Если в makefile есть правило, которое говорит как его обновлять (найденное либо в этом самом makefile, либо в другом), или если к нему применяется неявное правило (см. 10. Использование неявных правил), то при необходимости он будет обновлен. После того, как все makefile были проверены, если они были изменены, то make начнет все с чистого листа и заново запустит чтение файлов makefile (она также попытается обновить каждый из них снова, однако обычно этого не происходит, потому что обновление уже произошло). Каждый перезапуск приводит к обновлению специальной переменной MAKE_RESTARTS (см. 6.14. Другие специальные переменные).
Если вы знаете, что у один или нескольких ваших makefile не могут быть заново созданы, и вы хотите, чтобы make не выполняла неявный поиск правил в них (возможно по соображениям эффективности), то вы можете использовать обычный метод предотвращения поиска применения неявного правила. Например, вы можете написать явное правило в makefile в качестве target, и пустой рецепт (см. 5.9. Использование пустых рецептов).
Если makefile указывает правило двойного двоеточия для пересоздания файла с рецептом, но без prerequisites, то этот файл всегда будет заново создан (см. 4.13. Правила двойного двоеточия). В случае нескольких файлов makefile, makefile с правилом двойного двоеточия с рецептом, но без prerequisites, будет заново создан каждый раз когда запускается make, и затем make заново перечитывает файлы makefile. Это вызовет бесконечный цикл: make будет постоянно переделывать makefile и перезапускать, и больше ничего произойдет. Чтобы этого избежать, make не будет пытаться заново создать файлы makefile, которые указаны как цели двойного двоеточия с рецептом, но без prerequisites.
Псевдоцели (см. 4.6. Фальшивые цели (Phony Targets)) дают тот же эффект: они никогда не считаются актуальными, и поэтому подключенный файл файл, помеченный как phony, приведет к постоянному перезапуску. Чтобы избежать этого, make не будет пытаться пересоздавать файлы makefile, которые помечены как phony.
Вы можете воспользоваться этим, чтобы оптимизировать время запуска: если вы знаете, что вам не нужно, чтобы ваш Makefile был переделан, то можете запретить make попытки его переделать, путем добавления:
.PHONY: Makefile
.. или:
Makefile:: ;
Если вы не укажете какие-либо makefile для чтения с опциями -f или --file, утилита make попробует применить имена makefile по умолчанию; см. выше "3.2. Какое имя дать вашему Makefile". В отличие от файлов makefile, запрошенных опциями -f или --file, make не уверен, что эти makefile должны существовать. Однако, если файл makefile по умолчанию не существует, но может быть создан путем выполнения правил make, то возможно потребуется выполнить правила, чтобы можно было использовать makefile.
Таким образом, если ни один из файлов makefile не существует, make будет пытаться сделать каждый из них, пока ему не удастся сделать один из них. Обратите внимание, что не будет ошибкой, если make не сможет найти или создать makefile; файл makefile не всегда необходим.
Когда вы используете опцию -t или --touch (см. 9.3. Вместо выполнения recipes), вы не захотите использовать устаревший makefile, чтобы определить, к каким target прикоснуться. Так что опция -t не влияет на обновление файлов makefile; они действительно обновляются, даже если указана -t. Аналогично -q (или --question) и -n (или --just-print) не препятствуют обновлению файлов makefile, потому что устаревший makefile может привести к неправильному выводу для других target. Таким образом, make -f mfile -n foo приведет к обновлению mfile, его чтению, а затем напечатает рецепт для обновления foo и его prerequisites без его запуска. Рецепт, напечатанный для foo, будет указан в обновленном содержании mfile.
Тем не менее, иногда может действительно понадобиться предотвратить обновление файлов makefile. В можете достичь этого, указав файлы makefile как goals в командной строке, а также указав их в качестве makefile. Когда имя makefile явно указано как goal, к ним применяются опции -t и так далее.
Таким образом, 'make -f mfile -n mfile foo' будет читать файл mfile как makefile, напечатает рецепт, необходимый для его обновления без его реального запуска, и затем напечатает рецепт, необходимый для обновления foo без его запуска. Здесь рецепт для foo будет тем, что указан в существующем содержимом mfile.
3.6. Переопределение части другого Makefile
Иногда полезно иметь makefile, который почти такой же как другой makefile. Часто можно использовать директиву 'include', чтобы подключить один makefile в другой, и добавить больше target-ов и определений переменных. Однако недопустимо, чтобы два файла makefile давали разные рецепты для одной и той же цели. Но есть и другой способ.
В главном makefile (в том, который хочет подключить другой) вы можете использовать правило шаблона совпадения с чем угодно (match-anything pattern rule), чтобы указать, что пересоздание любой target не может быть сделано из информации в главном makefile, утилита должна искать её в другом makefile. См. 10.5. Определение и переопределение правил шаблона для дополнительной информации по правилам шаблона.
Например, если у вас есть файл makefile с именем Makefile, который говорит создать target 'foo' (и другие target-ы), вы можете написать makefile с именем GNUmakefile, содержащий следующее:
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force: ;
Если вы запустите команду 'make foo', то make будет искать GNUmakefile, прочитает его, и увидит, что для создания foo нужно запустить рецепт 'frobnicate > foo'. Если вы запустите команду 'make bar', утилита make не найдет в GNUmakefile варианта для создания bar, так что будет использовать рецепт из правила шаблона: 'make -f Makefile bar'. Если Makefile предоставит правило для обновления bar, то make применит это правило. И подобным образом для любой другой target, для которой GNUmakefile не указывает способ её создания.
Это работает так, что правило шаблона имеет шаблон в виде просто '%', потому что оно соответствует любой target. Правило задает prerequisite force для гарантии выполнения рецепта, даже если target-файл уже существует. Мы даем цели force пустой рецепт, чтобы не дать make искать неявное правило для его сборки - иначе применится то же правило match-anything для самого force и создастся цикл prerequisite!
3.7. Как make читает Makefile
GNU make выполняет свою работу в двух фазах. Во время первой фазы она читает все файлы makefile, подключенные makefile, и т. д., и инициализирует все внутренние переменные и их значения, и неявные и явные правила, и строит граф зависимостей всех target-ов и их prerequisites. Во время второй фазы make использует эти внутренние данные, чтобы определить, какие target-ы должны быть обновлены и запускает те рецепты, которые необходимы для их обновления.
Важно понимать этот двухфазный подход, потому что он оказывает прямое влияние на то, как происходит расширение переменных и функций; непонимание часто является причиной некоторой путаницы при написании файлов makefile. Ниже приводится сводка различных конструкций, которые можно найти в файле makefile, и фаза, в которой происходит расширение каждой части конструкции.
Мы говорим, что расширение немедленное, если оно происходит на первой фазе: make расширяет эту часть конструкции в момент парсинга makefile. Мы говорим, что расширение отложенное, если оно не неявное. Расширение отложенной части конструкта откладыватется до использования расширения: либо на него ссылаются в непосредственном контексте, либо когда это необходимо во время второй фазы.
В пока что вы можете быть незнакомы с некоторыми этими конструкциями. Вы можете возвращаться периодически к этой главе по мере знакомства с различными концепциями make в последующих главах.
Присваивание значения переменной. Определение переменной парсится следующим образом:
immediate = deferred
immediate ?= deferred
immediate := immediate
immediate ::= immediate
immediate :::= immediate-with-escape
immediate += deferred or immediate
immediate != immediate
define immediate
deferred endef
define immediate =
deferred endef
define immediate ?=
deferred endef
define immediate :=
immediate endef
define immediate ::=
immediate endef
define immediate :::=
immediate-with-escape endef
define immediate +=
deferred or immediate endef
define immediate !=
immediate endef
Для оператора добавления += правая часть считается немедленной (immediate), если переменная была ранее установлена как простая переменная (:= или ::=), и иначе считается отложенной (deferred).
Для оператора immediate-with-escape :::= значение в правой части немедленно расширяется, однако затем исключается (т. е. все экземпляры $ в результате расширения заменяются на $$).
Для оператора присваивания шелла != правая сторона вычисляется немедленно, и передается в шелл. Результат сохраняется в переменной, указанной в левой части, и эта переменная считается рекурсивно расширенной (и будет таким образом заново вычисляться при каждом обращении к ней).
Директивы условия (conditional directives). Директивы проверки условия парсятся непосредственно. Это означает, например, что в них не могут использоваться автоматические переменные, поскольку автоматические переменные не устанавливаются, пока не запустится рецепт для этого правила. Если вам нужно использовать автоматические переменные в conditional directive, то вы должны переместить её условие в рецепт, и использовать вместо этого conditional-синтаксис шелла.
Определение правила (Rule Definition). Правило всегда расширяется таким же образом, независимо от формы:
immediate : immediate ; deferred
deferred
Т. е. секции target и prerequisite расширяются немедленно, и рецепт, используемый для сборки, всегда откладывается (deferred). Это верно для явных правил (explicit rules), правил шаблона (pattern rules), правил суффикса (suffix rules), статических правил шаблона и простых определений prerequisite.
3.8. Как парсятся файлы Makefile
GNU make обрабатывает makefile построчно. Парсинг выполняется с использованием следующих шагов:
1. Полностью считывается логическая строка, включая дополнительные строки, экранированные backslash строки (см. 3.1.1. Разбиение длинных строк).
2. Удаляются комментарии (см. 3.1. Что содержат файлы Makefile).
3. Если строка начинается с символа префикса рецепта, и мы находимся в контексте правила, то строка добавляется к текущему рецепту, и считывается следующая строка (см. 5.1. Синтаксис рецепта).
4. Расширяются элементы строки, которые появляются в немедленном контексте расширения (immediate expansion context, см. 3.7. Как make читает Makefile).
5. Строка сканируется на предмет символов разделителя, таких как ':' или '=', чтобы определить, является ли строка присваиванием макроса или правилом (см. 5.1. Синтаксис рецепта).
6. Прочитанная строка усваивается и происходит чтение следующей строки.
Важным следствием этого является то, что макрос может быть расширен до целого правила, если он состоит из одной строки. Это будет работать:
myrule = target : ; echo built
$(myrule)
Однако следующее не сработает, потому что make не разделяет повторно строки после их расширения:
define myrule target:
echo built endef
$(myrule)
Показанный выше makefile приводит к определению цели 'target', где имеются prerequisites 'echo' и 'built', как если бы makefile содержал target: echo built вместо правила с рецептом. Символы новой строки (newlines), все еще присутствующие в строке после завершения развертывания, игнорируются как обычные пробелы.
Чтобы правильно развернуть многострочный макрос, вы должны использовать функцию eval: это заставит парсер утилиты make запуститься на результате расширения макроса (см. 8.10. Функция eval).
3.9. Вторичное расширение (Secondary Expansion)
Ранее мы узнали, что GNU make работает в двух разных фазах: фаза чтения (read-in phase) и фаза целевого обновления (target-update phase, см. 3.7. Как make читает Makefile). GNU make также имеет возможность разрешить вторичное расширение prerequisites (только) для всех или некоторых target, определенных в makefile. Для выполнения вторичного расширения перед первым списком prerequisite, использующим эту фичу, должна быть определена специальная цель .SECONDEXPANSION.
Если определена .SECONDEXPANSION, то когда GNU make нуждается в проверка prerequisites для target, эти prerequisites расширяются второй раз. В большинстве случаев это вторичное расширение не будет давать никакого эффекта, поскольку все ссылки на переменные и функции будут расширены во время первоначального разбора файлов makefile. Чтобы воспользоваться преимуществом фазы вторичного расширения парсера, необходимо в makefile избежать ссылки на переменную или функцию. В этом случае первое расширение просто отменяет привязку, но не расширяет её, и расширение оставляется для фазы вторичного расширения. Для примера рассмотрим такой makefile:
.SECONDEXPANSION:
ONEVAR = onefile
TWOVAR = twofile myfile: $(ONEVAR) $$(TWOVAR)
После фазы первого расширения список prerequisites цели myfile будет onefile и $(TWOVAR); первая (не экранированная) ссылка на переменную ONEVAR расширяется, в то время как вторая (экранированная) ссылка на переменную просто не экранируется без распознавания в качестве ссылки на переменную. Теперь во время вторичного расширения первое слово снова расширяется, однако поскольку в нем не содержится ссылки на переменную или функцию, то оно остается значением onefile, в то время как второе слово теперь нормальная ссылка на переменную TWOVAR, которая расширяется в значение twofile. Конечный результат: здесь два prerequisites, onefile и twofile.
Очевидно, что это не очень интересный случай, поскольку тот же результат можно было бы получить проще путем простого добавления двух не экранированных переменных в список prerequisites. Одно различие становится очевидным, если переменные сбрасываются; рассмотрим этот пример:
.SECONDEXPANSION:
AVAR = top onefile: $(AVAR) twofile: $$(AVAR)
AVAR = bottom
Здесь prerequisite onefile будет расширено немедленно, и разрешится в значение top, в то время как prerequisite twofile не будет полностью расширено до вторичного расширения, и даст значение bottom.
Настоящая сила этой фичи становится очевидной только когда вы обнаружите, что вторичные расширения всегда происходят в области действия автоматических переменных для этой target. Это означает, что вы можете использовать такие переменные, как $@, $* и т. д. во время вторичного расширения, и они будут иметь свои ожидаемые значения, точно такие же, как в рецепте. Все что вам нужно сделать, это отложить расширение с помощью экранирования $. Также вторичное расширение происходит как для явных, так и неявных правил (шаблона). С этими знаниями возможности использования фичи значительно возрастают. Например:
.SECONDEXPANSION:
main_OBJS := main.o try.o test.o
lib_OBJS := lib.o api.o
main lib: $$($$@_OBJS)
Здесь после начального расширения prerequisites обе цели main и lib будут $($@_OBJS). Во время вторичного расширения переменная $@ установится в имя цели, так что расширение для цели main будет $(main_OBJS), или main.o try.o test.o, в то время вторичное расширение для цели lib будет $(lib_OBJS), или lib.o api.o.
Вы можете также смешивать здесь функции, если они правильно экранированы:
main_SRCS := main.c try.c test.c
lib_SRCS := lib.c api.c
.SECONDEXPANSION:
main lib: $$(patsubst %.c,%.o,$$($$@_SRCS))
Эта версия позволяет указать исходные файлы вместо объектных, однако дает тот же результирующий список prerequisites, как в предыдущем примере.
Вычисление автоматических переменных во время фазы вторичного расширения, особенно целевой переменной имени $$@, ведет себя аналогично вычислению в рецептах. Тем не менее существуют некоторые тонкие отличия и "краевые случаи", которые вступают в игру для различных типов определений правил, которые понимает make. Ниже описаны тонкости использования различных автоматических переменных.
Вторичное расширение явных правил. Во время вторичного расширения явных правил (explicit rules) $$@ и $$% соответственно вычисляются в имя файла цели и, когда цель это член архива, в имя целевого элемента. Переменная $$< вычисляется в первое prerequisite в первом правиле для этой target. $$^ и $$+ вычисляются в список всех prerequisites правил, которые уже появились для той же target ($$+ с повторениями, и $$^ без повторений). Следующий пример поможет проиллюстрировать эти поведения:
.SECONDEXPANSION:
foo: foo.1 bar.1 $$< $$^ $$+ # строка #1
foo: foo.2 bar.2 $$< $$^ $$+ # строка #2
foo: foo.3 bar.3 $$< $$^ $$+ # строка #3
В первом списке prerequisite все три переменные ($$<, $$^ и $$+) расширяются в пустую строку. Во втором списке они получат значения foo.1, foo.1 bar.1 и foo.1 bar.1 соответственно. В третьем списке они получат значения foo.1, foo.1 bar.1 foo.2 bar.2, и foo.1 bar.1 foo.2 bar.2 foo.1 foo.1 bar.1 foo.1 bar.1 соответственно.
Правила подвергаются вторичному расширению в порядке makefile, за исключением того, что правило с рецептом всегда оценивается последним.
Переменные $$? и $$* недоступны, и расширяются в пустую строку.
Вторичное расширение правил статического шаблона. Правила вторичного расширения правил статического шаблона (static pattern rules) идентичны явным правилам, показанным выше, с единственным исключением: для правил статического шаблона переменная $$* устанавливается в основу шаблона (pattern stem). Как и для явных правил, переменная $$? недоступна, и расширяется в пустую строку.
Вторичное расширение неявных правил. Поскольку утилита make ищет неявное правило (implicit rule), она подставляет основу (stem) и затем выполняет вторичное расширение для каждого правила с соответствующим целевым шаблоном (target pattern). Значение автоматических переменных выводится таким же образом, как для правил статического шаблона. Например:
.SECONDEXPANSION:
foo: bar
foo foz: fo%: bo%
%oo: $$< $$^ $$+ $$*
Когда пробуется неявное правило для цели foo, переменная $$< расширяется в bar, $$^ расширяется в bar boo, $$+ также расширяется в bar boo, и $$* расширяется в f.
Обратите внимание, что префикс директории (D), как описано в 10.8. Алгоритм поиска неявного правила, присоединяется (после расширения) во все шаблоны списка prerequisites. Например:
.SECONDEXPANSION:
/tmp/foo.o:
%.o: $$(addsuffix /%.c,foo bar) foo.h
@echo $^
Распечатанный список prerequisite после вторичного расширения и реконструкции префикса директории будет /tmp/foo/foo.c /tmp/bar/foo.c foo.h. Если вам не нужна такая реконструкция, то в списке prerequisites можете использовать $$* вместо %.
[Словарик Makefile]
Для сохранения смысла некоторые термины оставлены как есть, без перевода. Ниже приведена попытка объяснения этих терминов.
goal это target, которую в конечном итоге стремятся обновить. Указать goal по умолчанию вы можете через командную строку (см. 9.2. Аргументы, указывающие goals) или с помощью специальной переменной .DEFAULT_GOAL (см. 6.14. Другие специальные переменные).
Makefile последовательность инструкций, которые управляют процессом сборки make.
prerequisite предпосылка: требования, или списки предварительных условий. Это список имен файлов, появляющихся после метки target и двоеточия. Список prerequisite обозначают зависимости для target,
recipe, рецепт последовательность строк операторов, которые идут за меткой target в Makefile.
rule запись в makefile, указывающая target и возможно через двоеточие список зависимостей этой target, см. 4.2. Синтаксис правила.
target метка, обозначающая целевой файл (файлы) сборки.
Картинка, поясняющая эти термины:

[Ссылки]
1. GNU make. 2. Zephyr: пакет CMake. 3. Язык программирования Rust. 4. Как запускать make. 5. Makefile: специальные встроенные имена целей. 6. Makefile: синтаксис команд цели (recipe syntax). 7. Магия makefile на простых примерах. 8. Зачем используется .PHONY в Makefile. 9. Makefile: ошибки в списке команд команд цели (recipe errors). |