Продолжение перевода руководства GNU make [1].
Примечание: объяснение некоторых специфических терминов make/makefile см. в разделе "Словарик Makefile", в конце статьи [2].
[10. Использование неявных правил]
Очень часто в разработке используются некоторые стандартные правила пересборки целевых файлов. Например, один из обычных способов создать объектный файл из файла исходного кода на языке C - применение компилятора C, программы cc.
Неявные правила (implicit rules) говорят make, как применять традиционные техники так, чтобы вы не описывали их подробно, когда хотите использовать. Например, существует неявное правило для компиляции C. Имена файлов определяют, какие неявные правила будут применены. Например, компиляция C обычно принимает файл исходного кода .c и генерирует объектный файл .o. Так что make применяет неявное правило для компиляции C, когда она видит эту комбинацию окончаний файлов.
Цепочка неявных правил может применяться в последовательности; например, make запустит повторную сборку .o-файла из файла .y способом .c-файла.
Встроенные неявные правила используют несколько переменных в своих рецептах, так что изменяя значения в этих переменных, вы можете влиять на то, как работают неявные правила. Например, переменная CFLAGS управляет флагами компилятора C для неявного правила компиляции C.
Вы можете определить свои собственные неявные правила написанием правил шаблона (pattern rules).
Правила суффикса являются более ограниченным способом определения неявных правил. Правила шаблона более общие и понятные, однако правила суффикса оставлены для совместимости.
10.1. Основы использования неявных правил
Чтобы позволить make найти обычный метод для обновления целевого файла, все что вам нужно сделать - отказаться от самостоятельного указания рецептов. Либо напишите правило без рецепта, либо вообще не пишите правило. Тогда make выяснит, какое неявное правило использовать на основе того, какой тип исходного файла существует или может быть создан.
Например предположим, что makefile выглядит так:
foo : foo.o bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
Поскольку вы упомянули foo.o, но не предоставили правила для него, утилита make автоматически найдет неявное правило, которое скажет, как его обновить. Это произойдет независимо от того, существует или нет файл foo.o в настоящий момент.
Если было найдено неявное правило, то оно может быть применено и к рецепту, и к одному или нескольким prerequisites (файлам исходного кода). Вы заходите написать правило для foo.o без рецепта, если необходимо указать дополнительные prerequisites, такие как заголовочные файлы, которые неявное правило не может предоставить.
Каждое неявное правило имеет целевой шаблон (target pattern) и prerequisite-шаблоны. Может быть несколько неявных правил с одним и тем же целевым шаблоном. Например, многие правила делают файлы '.o': одно из '.c' файла с помощью компилятора C; другое из '.p' файла с помощью компилятора Pascal; и так далее. Фактически будет применено правило, у которого существуют prerequisites, или они могут быть созданы. Так что если у вас есть файл foo.c, утилита make запустит компилятор C; иначе, если у вас есть файл foo.p, то make запустит компилятор Pascal; и так далее.
Конечно, при написании makefile вы знаете, какое неявное правило хотите использовать, и знаете, что оно будет выбрано, потому что известно, какие обязательные prerequisite-файлы должны существовать. См. 10.2. Каталог встроенных правил для списка всех предварительно определенных неявных правил.
Мы уже упоминали, что неявное правило применяется, если необходимые prerequisites "существуют или могут быть созданы". Файл "может быть создан" если он явно упомянут в makefile как целевой или как prerequisite, или если неявное правило для его создания может быть рекурсивно найдено для определения способа его создания. Когда неявная prerequisite является результатом другого неявного правила, мы говорим, что имеет место цепочка правил. См. 10.4. Цепочки неявных правил.
В общем случае утилита make ищет неявное правило для каждого целевого файла, и для каждого правила двух двоеточий (double-colon rule), у которого нет рецепта. Файл, который упомянут только как prerequisite, считается целевым объектом, правило которого ничего не указывает, поэтому для него происходит поиск неявных правил. См. 10.8. Алгоритм поиска неявного правила для подробного описания того, как выполняется этот поиск.
Обратите внимание, что явные explicit не влияют на поиск неявных правил. Например, рассмотрим следующее явное правило:
foo.o: foo.p
Здесь prerequisite foo.p не обязательно означает, что make будет пересоздавать foo.o в соответствии с неявным правилом для создания объектного файла .o из исходного файла Pascal .p. Например, если также существует foo.c, то вместо этого будет использовано неявное правило для создания объектного файла из исходного кода C, потому что оно появилось перед правилом Pascal в списке предварительно определенных неявных правил (см. 10.2. Каталог встроенных правил).
Если вы не хотите, чтобы неявное правило использовалось для целевого файла, у которого нет рецепта, то можете дать для этого целевого файла пустой рецепт путем написания точки с запятой (см. 5.9. Использование пустых рецептов).
10.2. Каталог встроенных правил
Здесь приведен каталог предварительно определенных неявных правил, которые всегда доступны, если makefile явно не переопределяет или отменяет их. См. 10.5.6. Отмена неявных правил для информации по отмене или переназначению неявного правила. Опция '-r' или '--no-builtin-rules' отменяет все предварительно определенные правила.
В этом руководстве описаны только правила по умолчанию, доступные на POSIX-совместимых операционных системах. Другие операционные системы, такие как VMS, Windows, OS/2 и т. д., могут иметь другой набор правил по умолчанию. Чтобы посмотреть полный список правил по умолчанию и переменных, доступный в вашей версии GNU make, запустите 'make -p' в директории, где нет файла makefile.
Не все эти правила будут всегда определены, даже когда не была указана опция -r. Многие предварительно определенные неявные правила реализованы в make как правила суффикса, поэтому какие именно правила определены, зависит от списка суффиксов (списка prerequisites специальной цели .SUFFIXES). Список суффиксов по умолчанию: .out, .a, .ln, .o, .c, .cc, .C, .cpp, .p, .f, .F, .m, .r, .y, .l, .ym, .lm, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el. Все неявные правила, описанные ниже, у которых prerequisites имеют один из этих суффиксов, на самом деле являются правилами суффиксов. Если вы измените список суффиксов, то единственными предопределенными правилами суффиксов будут правила, названные одним или двумя суффиксами из указанного вами списка; правила, суффиксы которых не включены в список, запрещаются. См. 10.7. Старомодные правила суффиксов для получения подробностей по правилам суффикса.
Компиляция программ C. Файл n.o будет автоматически сгенерирован из файла n.c на рецепте формы '$(CC) $(CPPFLAGS) $(CFLAGS) -c'.
Компиляция программ C++. Файл n.o будет автоматически сгенерирован из файла n.cc, n.cpp или n.C на рецепте формы '$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c'. Разработчики make рекомендуют использовать суффикс '.cc' или '.cpp' для файлов исходного кода C++ вместо файлов '.C' для лучшей поддержки систем, не чувствительных к регистру имен файлов и директорий (таких как Windows).
Компиляция программ Pascal. Файл n.o будет автоматически сгенерирован из файла n.p на рецепте '$(PC) $(PFLAGS) -c'.
Компиляция программ Fortran и Ratfor. Файл n.o будет автоматически сгенерирован из файла n.r, n.F или n.f путем запуска компилятора Fortran. Точный рецепт используется следующим образом:
‘.f’
‘$(FC) $(FFLAGS) -c’.
‘.F’
‘$(FC) $(FFLAGS) $(CPPFLAGS) -c’.
‘.r’
‘$(FC) $(FFLAGS) $(RFLAGS) -c’.
Препроцессинг программ Fortran и Ratfor. Файл n.f автоматически генерируется из файла n.r или n.F. Это правило только лишь запускает препроцессор для конвертации программы Ratfor или программы препроцессора Fortran в программу на строгом Fortran. Точный рецепт используется следующим образом:
‘.F’
‘$(FC) $(CPPFLAGS) $(FFLAGS) -F’.
‘.r’
‘$(FC) $(FFLAGS) $(RFLAGS) -F’.
Компиляция программ Modula-2. Файл n.sym генерируется из файла n.def на рецепте формы '$(M2C) $(M2FLAGS) $(DEFFLAGS)'. Файл n.o генерируется из файла n.mod; форма рецепта: '$(M2C) $(M2FLAGS) $(MODFLAGS)'.
Ассемблирование и препроцессинг программ assembler. Файл n.o автоматически генерируется из файла n.s путем запуска ассемблера as. Точный рецепт '$(AS) $(ASFLAGS)'.
Файл n.s автоматически генерируется из файла n.S запуском C-препроцессора cpp. Точный рецепт '$(CPP) $(CPPFLAGS)'.
Линковка одиночного объектного файла. Файл программы n автоматически генерируется из файла n.o запуском компилятора C для линковки программы. Используется точный рецепт '$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)'.
Это правило подходит для простой программы, где только один файл исходного кода. Оно также будет правильно работать, если здесь есть несколько объектных файлов (предположительно генерируемых из других файлов исходного кода), когда один из них имеет имя, совпадающее с именем исполняемого файла. Таким образом,
x: y.o z.o
.. когда существуют все файлы x.c, y.c и z.c, приведет к выполнению:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
В более сложных случаях, таких как когда не существует объектного файла, имя которого соответствует имени исполняемого файла, вы должны написать явный рецепт для линковки.
Каждый вид файла, автоматически созданный в объектные файлы '.o', автоматически линкуется с помощью компилятора ('$(CC)', '$(FC)' или '$(PC)'; компилятор C '$(CC)' используется для ассемблирования файлов '.s') без опции '-c'. Это можно сделать, используя объектные файлы '.o' в качестве промежуточных, но быстрее сделать компиляцию и линковку за один шаг, так что это обычно и делается.
Yacc для программ C. Файл n.c генерируется автоматически из файла n.y запуском Yacc с рецептом '$(YACC) $(YFLAGS)'.
Lex для программ C. Файл n.c генерируется автоматически из файла n.l запуском Lex. Актуальный рецепт '$(LEX) $(LFLAGS)'.
Lex для программ Ratfor. Файл n.r генерируется автоматически из n.l запуском Lex. Актуальный рецепт '$(LEX) $(LFLAGS)'.
Соглашение об использовании одинакового суффикса '.l' для всех Lex-файлов, независимо от того, производят ли они код C или код Ratfor, делает невозможным автоматическое определение того, какой из этих двух языков вы используете в каждом конкретном случае. Если make вызывается для пересоздания объектного файла из файла '.l', то make должна угадать, какой компилятор использовать. Она угадает компилятор C, потому что это более распространенный случай. Если вы используете Ratfor, то убедитесь, что make знает об этом, путем упоминания n.r в makefile. Или, если вы исключительно используете Ratfor, без файлов C, то удалите '.c' из списка суффиксов неявного правила следующим образом:
.SUFFIXES:
.SUFFIXES: .o .r .f .l ...
Создание Lint-библиотек из программ C, Yacc или Lex. Файл n.ln генерируется из n.c путем запуска lint. Точный рецепт '$(LINT) $(LINTFLAGS) $(CPPFLAGS) -i'. Такой же рецепт используется на коде C, сгенерированном из n.y или n.l.
TeX и Web. Файл n.dvi генерируется из n.tex на рецепте '$(TEX)'. Файл n.tex делается из n.web на рецепте '$(WEAVE)', или из n.w (и из n.ch, если он существует или может быть создан) на рецепте '$(CWEAVE)'. Файл n.p делается из n.web на рецепте '$(TANGLE)', и n.c делается из n.w (и из n.ch, если он существует или может быть создан) с рецептом '$(CTANGLE)'.
Texinfo и Info. Файл n.dvi делается из n.texinfo, n.texi или n.txinfo на рецепте '$(TEXI2DVI) $(TEXI2DVI_FLAGS)'. Файл n.info делается из n.texinfo, n.texi или n.txinfo на рецепте '$(MAKEINFO) $(MAKEINFO_FLAGS)'.
RCS. Любой файл n извлекается при необходимости из файла RCS с именем n.v или RCS/n.v. Используется точный рецепт '$(CO) $(COFLAGS)'. Файл n не будет извлечен из RCS, если он уже существует, даже если файл RCS более свежий. Правила для RCS являются терминальными (см. 10.5.5. Правила шаблона любого совпадения), так что файлы RCS не могут быть сгенерированы из другого источника; они должны реально существовать.
SCCS. Любой файл n извлекается при необходимости из файла SCCS с именем s.n или SCCS/s.n. Используется точный рецепт '$(GET) $(GFLAGS)'. Правила для SCCS являются терминальными (см. 10.5.5. Правила шаблона любого совпадения), так что файлы SCCS не могут быть сгенерированы из другого источника; они должны реально существовать.
В интересах SCCS файл n копируется из n.sh и делается исполняемым (всеми). Это для скиптов шелла, проверяемых в SCCS. Поскольку RCS сохраняет права на выполнение файла (execution permission), вам не нужно использовать эту фичу вместе с RCS.
Разработчики make рекомендуют избегать использования SCCS. RCS всеми считается отличным выбором, а также бесплатным. Выбирая свободное ПО вместо сопоставимых (или худших) проприетарных программ, вы поддерживаете принцип перехода на свободное программное обеспечение.
Общие замечания. Как правило, требуется поменять только переменные, перечисленные в списке выше, которые описаны в следующем разделе.
Однако рецепты во встроенных неявных правилах фактически используют переменные, такие как COMPILE.c, LINK.p и PREPROCESS.S, значения которых содержать перечисленные выше рецепты.
Утилита make придерживается соглашения, по которому правило для компиляции исходных файлов .x использует переменную COMPILE.x. Подобным образом правило для генерации исполняемого файла из файла .x использует LINK.x; и правило для препроцессинга файла .x использует PREPROCESS.x.
Каждое правило, которое генерирует объектный файл, использует переменную OUTPUT_OPTION. Утилита make определяет эту переменную либо содержащую '-o $@', либо пустой, в зависимости от опции времени компиляции. Вам нужна опция '-o', чтобы гарантировать, что вывод произойдет в правильный файл, когда исходный файл находится в другой директории, ка при использовании VPATH (см. 4.5. Директории поиска для prerequisites). Однако компиляторы на некоторых системах не принимают опцию '-o' для объектных файлов. Если вы используете такую систему, и используете VPATH, то некоторые компиляции будут помещать свой вывод в неправильное место. Возможное решение этой проблемы заключается в том, чтобы дать переменной OUTPUT_OPTION значение '; mv $*.o $@'.
10.3. Переменные, используемые неявными правилами
Рецепты встроенных неявных правил либерально используют предварительно определенные переменные. Вы можете либо изменить значение этих переменных в makefile, либо аргументами для make, либо в окружении, чтобы поменять поведение неявных правил без переопределения их самих. Вы можете отменить все переменные, используемые неявными правилами, с помощью опции '-R' или '--no-builtin-variables'.
Например рецепт, используемый для компиляции исходного кода C, фактически говорит '$(CC) -c $(CFLAGS) $(CPPFLAGS)'. Значениями по умолчанию используемых переменных являются 'cc' и ничего, что в результате даст команду 'cc -c'. Путем переопределения переменной CC на 'ncc' вы можете использовать 'ncc' для всех C-компиляций, выполняемых неявным правилом. Переопределением 'CFLAGS' в '-g' вы можете передать опцию '-g' для каждой компиляции. Все неявные правила компиляции C используют '$(CC)', чтобы получить имя программы для компилятора, и все включают '$(CFLAGS)' в число аргументов, предоставляемых компилятору.
Переменные, используемые в неявных правилах, делятся на 2 класса: те, которые являются именами программ (наподобие CC), и те, которые содержат аргументы для программ (наподобие CFLAGS) ("имя программы" может также содержать некоторые аргументы команды, однако оно должно начинаться на фактическое имя файла исполняемой программы). Если значение переменной содержит больше одного аргумента, то они отделяются друг от друга пробелами.
Следующая таблица описывает некоторые наиболее часто используемые предварительно определенные переменные. Этот список не является исчерпывающим, и указанные здесь значения по умолчанию могут не соответствовать тому, что выбирает make для вашего окружения. Чтобы увидеть полный список предварительно определенных переменных для вашего экземпляра GNU make, вы можете запустить 'make -p' в директории, где нет файла makefile.
Вот таблица некоторых наиболее распространенных переменных, используемых в качестве имен программ во встроенных правилах:
AR Программа для обслуживания архивов (библиотек); по умолчанию 'ar'.
AS Программа для компиляции файлов ассемблера; по умолчанию 'as'.
CC Программа для компиляции программ C; по умолчанию 'cc'.
CXX Программа для компиляции программ C++; по умолчанию 'g++'.
CPP Программа для запуска препроцессора C, которая выводит результаты в стандартный вывод; по умолчанию '$(CC) -E'.
FC Программа для компиляции программ или препроцессинга Fortran и Ratfor; по умолчанию 'f77'.
M2C Программа, используемая для компиляции исходного кода Modula-2; по умолчанию 'm2c'.
PC Программа для компиляции программ Pascal; по умолчанию 'pc'.
CO Программа для извлечения файла из RCS; по умолчанию 'co'.
GET Программа для извлечения файла из SCCS; по умолчанию 'get'.
LEX Программа для конвертации грамматики Lex в исходный код; по умолчанию 'lex'.
YACC Программа для конвертации грамматики Yacc в исходный код; по умолчанию 'yacc'.
LINT Программа для запуска lint на исходном коде; по умолчанию 'lint'.
MAKEINFO Программа для конвертации исходного файла Texinfo в файл Info; по умолчанию 'makeinfo'.
TEX Программа для создания файлов TeX DVI из исходника TeX; по умолчанию 'tex'.
TEXI2DVI Программа для создания файлов TeX DVI из исходника Texinfo; по умолчанию 'texi2dvi'.
WEAVE Программа для трансляции Web в TeX; по умолчанию 'weave'.
CWEAVE Программа для трансляции C Web в TeX; по умолчанию 'cweave'.
TANGLE Программа для трансляции Web в Pascal; по умолчанию 'tangle'.
CTANGLE Программа для трансляции C Web в C; по умолчанию 'ctangle'.
RM Команда для удаления файла; по умолчанию 'rm -f'.
Ниже приведена таблица переменных, значения которых представляют дополнительные аргументы для перечисленных выше программ. Значение по умолчанию у них всех пустая строка, если не указано что-либо иное.
ARFLAGS Флаги (опции) для программы поддержки архивов (библиотек); по умолчанию 'rv'.
ASFLAGS Дополнительные флаги (опции) для assembler (when explicitly invoked on a ‘.s' or ‘.S' file).
CFLAGS Дополнительные флаги (опции) для компилятора C.
CXXFLAGS Дополнительные флаги (опции) для компилятора C++.
COFLAGS Дополнительные флаги (опции) для программы RCS co.
CPPFLAGS Дополнительные флаги (опции) для препроцессора C и программ, которые его используют (компиляторы C и Fortran).
FFLAGS Дополнительные флаги (опции) для компилятора Fortran.
GFLAGS Дополнительные флаги (опции) для программы SCCS get.
LDFLAGS Дополнительные флаги (опции) для компиляторов, когда предполагается вовлечение ими линкера 'ld', такие как -L. Библиотеки (-lfoo) должны быть вместо этого добавлены в переменную LDLIBS.
LDLIBS Флаги (опции) библиотек или имена, предоставляемые компиляторам, когда предполагается вовлечение ими линкера 'ld'. Переменная LOADLIBES является устаревшей (однако все еще поддерживаемой) альтернативой LDLIBS. Не библиотечные флаги линкера, такие как -L, должны быть помещены в переменную LDFLAGS.
LFLAGS Дополнительные флаги (опции), предоставляемые для Lex.
YFLAGS Дополнительные флаги (опции), предоставляемые для Yacc.
PFLAGS Дополнительные флаги (опции) для компилятора Pascal.
RFLAGS Дополнительные флаги (опции) для компилятора Fortran, используемого для программ Ratfor.
LINTFLAGS Дополнительные флаги (опции), предоставляемые для lint.
10.4. Цепочки неявных правил
Иногда файл может быть создан последовательностью неявных правил. Например, файл n.o можно сделать из n.y запуском сначала Yacc, и затем cc. Такая последовательность называется цепочкой.
Если файл n.c существует, или был упомянут в makefile, то не требуется специальный поиск: make обнаружит, что объектный файл может быть сделан C-компиляцией из n.c; в дальнейшем, при рассмотрении того, как сделать n.c, используется правило для запуска Yacc. В конечном итоге обновляются как n.c, так и n.o.
Однако, даже если n.c не существует и не упомянут, утилита make знает, как предоставить его в качестве промежуточного звена между n.o и n.y! В этом случае n.c называется промежуточным файлом. Как только make определила использование промежуточного файла, это вводится в базу данных, как если бы было упомянуто в makefile, вместе с неявным правилом, которое говорит, как его создать.
Промежуточные файлы пересоздаются с использованием их правил точно так же, как и все другие правила. Однако промежуточные файлы обрабатываются по-разному двумя способами.
Первое отличие заключается в том, что происходит, если промежуточный файл не существует. Если обычный файл b не существует, и make рассматривает целевой объект, зависящий от b, то она неизменно создает b и затем обновляет целевой объект, используя b. Однако если b это промежуточный файл, то make оставить его одного достаточно правильным образом: она не создаст b, если ни одна из его prerequisites не является устаревшей. Это означает, что целевой объект, зависящий от b, также не будет пересобран, если нет какой-либо другой причины для обновления этого целевого объекта: например, целевой объект не существует, или другая prerequisite более свежая, чем целевой объект.
Второе отличие заключается в том, что если make создает b, чтобы обновить что-то еще, то она впоследствии удалит b после того, как он больше не нужен. Поэтому промежуточный файл, который не существовал до перед работой make, также не будет существовать после работы make. Утилита make сообщает вам об удалении, печатая команду 'rm', показывающую какой файл удаляется.
Вы можете явно пометить файл как промежуточный путем перечисления его в качестве prerequisite специальной цели .INTERMEDIATE. Это оказывает эффект, даже если файл явно упомянут каким-то другим способом.
Файл не может быть промежуточным, если он был упомянут в makefile как целевой объект или prerequisite, так что один из способов избежать удаления промежуточных файлов - добавить их как prerequisite к какому-нибудь целевому объекту. Однако если поступить таким образом, то это заставит make выполнять дополнительную работу, когда происходит поиск по правилам шаблона (см. 10.8. Алгоритм поиска неявного правила).
Как альтернатива, перечисление файла как prerequisite специальной цели .NOTINTERMEDIATE заставляет его не считаться промежуточным (точно так же как и любое другое упоминание файла). Кроме того, перечисление целевого шаблона правила шаблона в качестве prerequisite цели .NOTINTERMEDIATE гарантирует, что целевые объекты, генерируемые с использованием этого правила шаблона, не будут считаться промежуточными.
Вы можете полностью запретить промежуточные файлы в своем makefile путем предоставления .NOTINTERMEDIATE в качестве целевого объекта без prerequisites: в таком случае это будет применено к каждому файлу в makefile.
Если вы не хотите, чтобы make создавала файл только потому, что он еще не существует, однако также не хотите, чтобы make автоматически удаляла файл, то можете пометить его как вторичный (secondary file). Для этого перечислите его в качестве prerequisite специальной цели .SECONDARY. Если пометить файл как вторичный, то это также помечает его как промежуточный.
Цепочка может вовлекать больше двух неявных правил. Например, можно сделать файл foo из RCS/foo.y.v запуском RCS, Yacc и cc. Затем оба файла foo.y и foo.c будут промежуточными, и при завершении будут удалены.
Ни одно неявное правило не может появиться в цепочке более одного раза. Это означает, что make даже не будет рассматривать такую нелепую вещь, как создание foo из foo.o.o путем двойного запуска линкера. Это ограничение дает дополнительное преимущество, заключающееся в предотвращении любого бесконечного цикла в поиске по цепочке неявных правил.
Существуют специальные неявные правила для оптимизации определенных случаев, которые иначе обрабатывались бы цепочками правил. Например, создание foo из foo.c может быть обработано компиляцией и линковкой с отдельными цепочечными правилами, используя foo.o в качестве промежуточного файла. Но на самом деле происходит то, что специальное правило для этого случая делает компиляцию и линковку одной командой cc. Оптимизированное правило используется вместо пошаговой цепочки, потому что оно появляется раньше в списке правил.
И наконец, по соображениям производительности make не будет учитывать нетерминальные правила любого совпадения (match-anything rules, например, '%:') при поиске правила для сборки prerequisite неявного правила (см. 10.5.5. Правила шаблона любого совпадения).
10.5. Определение и переопределение правил шаблона
Вы определяете неявное правило (implicit rule) путем написания правила шаблона (pattern rule). Правило шаблона выглядит наподобие обычного правила, за исключением того, что целевой объект содержит символ '%' (только его одного). Целевой объект считается шаблоном для совпавших имен файлов; '%' может совпасть с любой не пустой подстрокой, в то время как любые другие символы совпадают только сами с собой. В prerequisites подобным образом используются '%' чтобы показать, как их имена соотносятся с именем целевого объекта.
Таким образом, правило шаблона '%.o : %.c' говорит, как создать любой файл stem.o из любого файла stem.c.
Обратите внимание, что расширение с помощью '%' в правилах шаблона происходит после любого расширения переменной или функции, которое происходит при чтении makefile. См. 6. Как использовать переменные в makefile и 8. Функции для преобразования текста.
10.5.1. Введение в правила шаблона. Правило шаблона (pattern) содержит символ '%' (только его одного) в целевом объекте; иначе оно выглядит точно так же, как обычное правило. Целевой объект (target) это шаблон для совпадения имен файлов; '%' совпадает с любой не пустой подстрокой, в то время как другие символы совпадают только сами с собой.
Например '%.c' в качестве шаблона совпадет с любым именем файла, оканчивающимся на '.c'. 's.%.c' в качестве шаблона совпадет с любым именем файла, который начинается на 's.', оканчивается на '.c', и которое длиной как минимум 5 символов (как минимум один символ должен совпасть с '%'). Подстрока, которая совпала с '%', называется основой (stem).
'%' в prerequisite правила шаблона означает тот же stem, которому соответствует '%' в целевом объекте. Чтобы применилось правило шаблона, его целевой шаблон должен совпасть с рассматриваемым именем файла, и все его prerequisites (после подстановки шаблона) должны содержать имена файлов, которые существуют или могут быть созданы. Эти файлы становятся prerequisites целевого объекта.
Таким образом, правило следующей формы:
%.o : %.c ; recipe...
.. определяет способ создания файла n.o с другим файлом n.c в качестве его prerequisite, при условии, что файл n.c существует или может быть создан.
Также могут быть prerequisites, которые не используют '%'; такая prerequisite прикрепляется к каждому файлу, созданному этим правилом шаблона. Эти не изменяющиеся prerequisites иногда полезны.
Правило шаблона не обязательно должно иметь какие-либо prerequisites, содержащие '%', или фактически любые предпосылки как таковые. Такое правило фактически является общим подстановочным (general wildcard). Оно предоставляет способ создать любой файл, который совпадет с целевым шаблоном. См. 10.6. Определение правил по умолчанию для последней инстанции.
С целевым объектом может совпасть больше чем одно правило шаблона. В этом случае make выберет "самое подходящее" (best fit) правило. См. 10.5.4. Как работает совпадение шаблонов.
Целевой объект может соответствовать больше чем одному правилу шаблона; однако каждый целевой объект должен содержать символ %. Несколько целевых шаблонов в правилах шаблона всегда обрабатываются как групповые цели (grouped targets, см. 4.10. Несколько целей в правиле), независимо от того, используют ли они разделитель : или &:.
Существует одно исключение: если целевой объект шаблона устарел или не существует, и makefile не нуждается в его сборке, то это не приведет к тому, что другие целевые объекты будут считаться устаревшими. Обратите внимание, что это историческое исключение будет удалено в будущих версиях GNU make, и на него не следует полагаться. Если эта ситуация была обнаружена утилитой make, то будет сгенерировано предупреждение, что рецепт шаблона не обновит соответствующий целевой объект; однако make не может детектировать все такие ситуации. Пожалуйста убедитесь, что ваш рецепт обновляет все целевые шаблоны, когда запускается.
10.5.2. Примеры правил шаблона. Вот несколько правил шаблона, фактически предопределенных в make. Во первых, правило, которое компилирует файлы '.c' в файлы '.o':
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
.. определяет правило, которое может создать любой файл x.o из x.c. Рецепт использует автоматические переменные '$@' и '$< ' для подстановки имен целевого файла и исходного файла в каждом случае, где это правило применяется (см. 10.5.3. Автоматические переменные make).
Вот второе встроенное правило:
% :: RCS/%,v
$(CO) $(COFLAGS) $<
Это определит правило, которое может создать любой файл x из соответствующего файла x.v в подкаталоге RCS. Поскольку целевой объект '%', то это правило будет применено к любому файлу при условии, что существует соответствующий файл prerequisite. Два двоеточия делают правило терминальным: это означает, что его prerequisite может быть не промежуточный файл (см. 10.5.5. Правила шаблона любого совпадения).
У этого правила шаблона два целевых объекта:
%.tab.c %.tab.h: %.y
bison -d $<
Это говорит make, что рецепт 'bison -d x.y' создаст оба файла x.tab.c и x.tab.h. Если файл foo зависит от файлов parse.tab.o и scan.o, и файл scan.o зависит от файла parse.tab.h, то когда меняется parse.y, рецепт 'bison -d parse.y' будет выполнен только один раз, и prerequisites обоих файлов parse.tab.o и scan.o будут удовлетворены (предположительно, файл parse.tab.o будет перекомпилирован из parse.tab.c, и файл scan.o из scan.c, в то время как foo линкуется из parse.tab.o, scan.o, и его других prerequisites, что выполнится долго и счастливо).
10.5.3. Автоматические переменные make. Предположим, что вы пишете правило шаблона для компиляции файла '.c' в файл '.o': как вы напишете команду 'cc' так, чтобы она работала с правильным именем исходного файла? Вы не можете написать правило в рецепте, потому что имя будет отличаться каждый раз, когда применяется неявное правило.
В этом случае вы используете специальную фичу make, автоматические переменные. Значения этих переменных вычисляются заново для каждого выполняемого правила, основываясь на целевом объекте и prerequisites правила. В этом примере для имени объектного файла используется '$@', и '$< ' для имени исходного файла.
Очень важно понимать ограниченную область, в которой доступны значения автоматических переменных: значения у них имеются только внутри рецепта. В частности, вы не можете использовать их в любом месте целевого списка правила; они не имеют никакого значения, и будут расширены до пустой строки. Кроме того, они не могут быть доступны непосредственно в списке prerequisites правила. Общей ошибкой является попытка использовать $@ внутри списка prerequisites; это не будет работать. Тем не менее существует специальная фича GNU make, secondary expansion (см. 3.9. Вторичное расширение (Secondary Expansion)), которая позволяет использовать значения автоматических переменных в списках prerequisite.
Вот таблица автоматических переменных:
$@ Имя файла целевого объекта правила. Если целевой объект находится в файле библиотеки (archive member), то '$@' это имя файла архива. В правиле шаблона, у которого несколько целевых объектов (см. 10.5.1. Введение в правила шаблона), '$@' это имя того целевого объекта, который вызвал запуск рецепта правила.
$% Имя целевого элемента, когда целевой объект находится в библиотеке (archive member). См. 11. Использование make для обновления файлов архива (библиотеки). Например, если целевой объект foo.a(bar.o), то '$%' это bar.o, и '$@' это архив (библиотека) foo.a. '$%' пустая, когда целевой объект не находится в библиотеке.
$< Имя первого prerequisite. Если целевой объект получил рецепт из неявного правила, то это будет первая prerequisite, добавленная неявным правилом (см. 10. Использование неявных правил).
$? Имена всех prerequisites, которые более свежие, чем целевой объект, отделенные друг от друга пробелами. Если целевой объект не существует, то в $? добавятся все prerequisites. Для prerequisites, которые являются членами архива, используются только именованные элементы архива (см. 11. Использование make для обновления файлов архива (библиотеки)).
Переменная '$?' полезна даже к явных правилах, когда вы хотите оперировать только теми prerequisites, которые изменились. Например предположим, что архив с именем lib должен содержать копии нескольких объектных файлов. Это правило просто копирует измененные объектные файлы в архив:
lib: foo.o bar.o lose.o win.o
ar r lib $?
$^ Имена всех prerequisites, с пробелами между ними. Для prerequisites, которые являются членами архива, используются только именованные элементы архива (см. 11. Использование make для обновления файлов архива (библиотеки)). Целевой объект имеет только одну prerequisite на каждом другом файле, от которого он зависит, независимо от того, сколько раз каждый файл перечислен в качестве prerequisite. Так что если вы укажете ваш список prerequisite для целевого объекта больше одного раза, значение $^ будет содержать только одну копию имени. Этот список не содержит какие-либо order-only prerequisites; для таких см. далее переменную '$|'.
$+ Это работает наподобие '$^', однако prerequisites, перечисленные больше одного раза, дублируются в том порядке, в каком перечислены в makefile. Это в первую очередь полезно для использования в командах линковки, где имеет значение повторения имен библиотечных файлов в необходимом правильном порядке.
$| Имена всех order-only prerequisites, с пробелами между ними.
$* Основа (stem), с которой совпало неявное правило (см. 10.5.4. Как работает совпадение шаблонов). Если целевой объект это dir/a.foo.b, и целевой шаблон a.%.b, то stem будет dir/foo. Stem полезен для конструирования имен связанных файлов.
В правиле статического шаблона stem-часть имени файла, которая совпала с '%' в целевом шаблоне.
В явном правиле нет stem-а; так что '$*' не может быть определен таким способом. Вместо этого, если имя целевого объекта заканчивается на распознанный суффикс (см. 10.7. Старомодные правила суффиксов), '$*' устанавливается в имя целевого объекта минус суффикс. Например, если имя цели 'foo.c', то '$*' установится в 'foo', поскольку '.c' является суффиксом. GNU make делает эту странную вещь только для совместимости с другими реализациями make. Вам следует избегать использования '$*', за исключением применения в неявных правилах или правилах статического шаблона.
Если имя целевого объекта в явном правиле не оканчивается на распознанный суффикс, то '$*' установится в пустую строку для этого правила.
Из перечисленных выше переменных 4 имеют значения одиночных имен файлов, а 3 имеют значения в виде списков имен файлов. У этих семи переменных есть варианты, которые соответствуют только имени директории, или только имени файла в директории. Вариант имен переменных формируются добавлением 'D' или 'F' соответственно. Функции dir и notdir могут использоваться для получения подобного эффекта (см. 8.3. Функции для имен файлов). Однако следует отметить, что все варианты 'D' опускают завершающий слеш, который всегда появляется на выходе функции dir. Вот таблица вариантов автоматических переменных:
'$(@D)' Часть имени целевого объекта, относящаяся к директории, с удаленным слешем в конце. Например, если '$@' это dir/foo.o, то '$(@D)' это dir. Это значение будет ., если '$@' не содержит слеш.
'$(@F)' Часть имени целевого объекта, относящаяся только к имени файла внутри директории. Например, если '$@' это dir/foo.o, то '$(@F)' это foo.o. '$(@F)' это эквивалент '$(notdir $@)'.
‘$(*D)' ‘$(*F)' Часть stem, содержащая путь директории и имя файла внутри директории без суффикса; для примера выше это dir и foo.
‘$(%D)' ‘$(%F)' Части имени элемента архива целевого объекта, относящиеся к директории и имени внутри директории. Это имеет смысл только для целевых объектов - элементов архива (библиотеки), и полезно только когда элемент архива может содержать имя директории (см. 11.1. Элементы архива в качестве целевых объектов).
‘$(< D)' ‘$(< F)' Части директории и имени файла внутри директории первого prerequisite.
‘$(^D)' ‘$(^F)' Список директорий и имен файлов внутри директорий всех prerequisites.
‘$(+D)' ‘$(+F)' Список директорий и имен файлов внутри директорий всех prerequisites, включая несколько экземпляров дублированных prerequisites.
‘$(?D)' ‘$(?F)' Список директорий и имен файлов внутри директорий всех prerequisites, которые более свежие, чем файл целевого объекта.
Обратите внимание, что используется специальное стилистическое соглашение, когда мы говорим про эти автоматические переменные; мы пишем "значение '$< '" вместо "переменная < ", как мы написали бы для обычных переменных, таких как objects и CFLAGS. Разработчики make думают, что это соглашение выглядит более натуральным для этого специального случая. Пожалуйста не думайте, что это имеет глубокий смысл; '$< ' относится к переменной с именем < точно так же как '$(CFLAGS)' относится к переменной с именем CFLAGS. Вы также можете использовать '$(< )' вместо '$< '.
10.5.4. Как работает совпадение шаблонов. Целевой шаблон состоит из '%' между префиксом и суффиксом, причем как любой из них, так и они оба, могут быть пустыми. Шаблон совпадет с именем файла только если имя файла начинается с префикса и заканчивается суффиксом, без наложения. Текст между префиксом и суффиксом называется основой (stem). Таким образом, когда шаблон '%.o' совпадет с именем файла test.o, и для него stem будет 'test'. Prerequisites правила шаблона превращаются в фактические имена файлов путем подстановки stem вместо символа '%'. Таким образом, если в том же примере одна из prerequisites записана как '%.c', то это расширится в 'test.c'.
Когда целевой шаблон не содержит слеш (и обычно это так), имена директорий в именах файлов удаляются из имени файла перед тем, как произойдет сравнение с префиксом и суффиксом целевого файла. После сравнения имени файла с целевым шаблоном, имена директорий вместе со слешем у них в конце добавляются к именам файлов prerequisite, генерируемым из из шаблонов prerequisite правила шаблона, и имени файла. Директории игнорируется только для нахождения неявного правила для использования, а не для применения этого правила. Таким образом, шаблон 'e%t' совпадет с именем файла src/eat, где 'src/a' выступает в качестве stem. Когда prerequisites превращаются в имена файлов, директории из stem добавляются спереди, в то время как остальная часть stem подставляется вместо '%'. Так что stem 'src/a' для prerequisite-шаблона 'c%r' даст имя файла src/car.
Правило шаблона может использоваться для построения данного файла только если существует целевой шаблон, который совпадет с именем файла, и все prerequisites в этом правиле либо существуют, либо могут быть построены. Правила, которые вы пишете, имеют приоритет надо встроенными. Однако имейте в виду, что правило, которое удовлетворено без составления цепочки неявных правил (например правило, которое не имеет prerequisites, или его prerequisites уже существуют или упомянуты), всегда имеет приоритет над правилом с prerequisites, которые должны быть созданы путем цепочки других неявных правил.
Возможно, что этим критериям будет соответствовать больше одного правила шаблона. В этом случае make выберет правило с самым коротким stem (т. е. шаблоном, который соответствует наиболее точно). Если больше одного правила шаблона имеет самый короткий stem, то make выберет первый из тех, что нашла в makefile.
Этот алгоритм приводит к тому, что более конкретные правила предпочтительнее более общих; например:
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
%.o : %.f
$(COMPILE.F) $(OUTPUT_OPTION) $<
lib/%.o: lib/%.c
$(CC) -fPIC -c $(CFLAGS) $(CPPFLAGS) $< -o $@
С учетом этих правил, когда произошел запрос на сборку bar.o, когда существуют и bar.c, и bar.f, утилита make выберет первое правило и скомпилирует bar.c в bar.o. В той же самой ситуации, где bar.c не существует, make выберет второе правило и скомпилирует bar.f в bar.o.
Если make попросили собрать lib/bar.o, и существуют оба файла lib/bar.c и lib/bar.f, то будет выбрано третье правило, поскольку stem для этого правила ('bar') короче, чем stem для первого правила ('lib/bar'). Если lib/bar.c не существует, то третье правило не подойдет, и будет использоваться второе правило, даже при том, что stem длиннее.
10.5.5. Правила шаблона любого совпадения. Когда правило целевого шаблона содержит только '%', оно соответствует любому имени файла. Мы называем такие правила правилами любого совпадения (match-anything rules). Они очень полезны, но может понадобиться много времени, чтобы заставить задуматься о них, потому что нужно учитывать каждое такое правило для каждого имени файла, указанного либо в качестве целевого объекта, либо в качестве prerequisite.
Предположим, что makefile упоминает foo.c. Для этого целевого объекта утилите make придется рассмотреть вопрос о том, чтобы сделать это путем линковки объектного файла foo.c.o, или путем C компиляции-и-линковки за один шаг из foo.c.c, или путем Pascal компиляции-и-линковки из foo.c.p, и многие другие возможности.
Мы знаем, что эти возможности абсурдны, поскольку foo.c это файл исходного кода C, а не исполняемый файл. Если бы make рассмотрела бы эти возможности, то они в конечном итоге были бы отвергнуты, потому что такие файлы, как foo.c.o и foo.c.p не существуют. Но эти возможности настолько многочисленны, что make работала бы очень медленно, если их все необходимо рассмотреть.
Чтобы ускорить работу make, были внесены различные ограничения на то, как make применяет правила match-anything. Можно применить два различных ограничения, и каждый раз, когда вы определяете match-anything правило, необходимо выбрать одно их них для этого правила.
Один из вариантов выбора - пометить match-anything правило как терминальное, путем его определения с двумя символами двоеточия. Когда правило терминальное, оно не применяется, если его prerequisites не существуют. Prerequisites, которые могут быть сделаны другими неявными правилами, недостаточно хороши. Другими словами, за пределами терминального правила дальнейшая обработка цепочек правил не допускается.
Например, встроенные неявные правила для извлечения исходников из файлов RCS и SCCS являются терминальными; в результате если файл foo.c,v не существует, то make даже не будет пытаться создать его как промежуточный файл из foo.c,v.o или из RCS/SCCS/s.foo.c,v. Файлы RCS и SCCS как правило являются конечными исходными файлами, которые не следует пересоздавать из других файлов; поэтому make может экономить время, не рассматривая способы их пересборки.
Если вы не пометили match-anything как терминальное, то оно будет нетерминальным. Нетерминальное match-anything правило не может применяться к prerequisite неявного правила, или к имени файла, которое указывает конкретный тип данных. Имя файла указывает определенный тип данных, если ему соответствует какой-либо целевой объект, совпавший с некоторым неявным правилом, которое не match-anything.
Например, имя файла foo.c совпадает с целевым объектом для правила шаблона '%.c : %.y' (правило для запуска Yacc). Независимо от того, действительно ли это правило применимо (что происходит только при наличии файла foo.y), тот факт, что его целевой объект совпал, достаточен для предотвращения рассмотрения любых нетерминальных match-anything правил лля файла foo.c. Таким образом, make даже не будет рассматривать попытку сделать foo.c исполняемым файлом из foo.c.o, foo.c.c, foo.c.p, и т. п.
Мотивация для этого ограничения заключается в том, что нетерминальные match-anything правила используются для создания файлов содержащих определенный тип данных (таких как исполняемые файлы), и имя файла с распознанным суффиксом показывает на некоторый другой специальный тип данных (такой как файл исходного кода C).
Специальные встроенные dummy-правила шаблона предоставляются исключительно для распознавания определенных имен файлов, так что нетерминальные match-anything правила не будут учитываться. Эти dummy-правила не имеют никаких предпосылок и рецептов, и они игнорируются для всех других применений. Например, встроенное неявное правило:
%.p :
.. существует чтобы убедиться, что исходные файлы Pascal, такие как foo.p, совпали со специальным целевым шаблоном, и тем самым была предотвращена потеря времени на поиск файлов foo.p.o или foo.p.c.
Dummy-правила шаблона, такие как '%.p', создаются для каждого суффикса, указанного как допустимый для использования в правилах суффикса (см. 10.7. Старомодные правила суффиксов).
10.5.6. Отмена неявных правил. Вы можете переназначить встроенное (или уже определенное вами) неявное правило путем определения нового правила шаблона с такими же целевым объектом и prerequisites, но с другим рецептом. Когда определено новое правило, оно заменяет встроенное. Позиция нового правила в последовательности неявных правил определяется местом, где вы написали новое правило.
Вы можете отменить встроенное неявное правило путем определения правила шаблона с такими же целевым объектом и prerequisites, но без рецепта. Например, следующее отменит правило, которое запускает ассемблер:
%.o : %.s
10.6. Определение правил по умолчанию для последней инстанции
Вы можете определить неявное правило последней инстанции (last-resort implicit rule) написанием терминального match-anything правила шаблона без prerequisites (см. 10.5.5. Правила шаблона любого совпадения). Это работает почти так же, как и любое другое правило шаблона; единственная особенность в том, что оно будет соответствовать любому целевому объекту. Таким образом, рецепт такого правила используется для всех целевых объектов и prerequisites, у которых нет собственного рецепта, и для которых не применяется другое неявное правило.
Например, когда тестируется makefile, вы не нужно заботиться о том, чтобы исходные файлы содержали реальные данные, нужно только чтобы они существовали. Тогда вы можете это реализовать так:
%::
touch $@
.. это приведет к автоматическому созданию всех необходимых исходных файлов (в качестве prerequisites).
Вы можете вместо этого определить рецепт, используемый для целевых объектов, у которых вообще нет правил, даже тех, которые не указывают рецепты. Для этого нужно написать правило для цели .DEFAULT. Рецепт такого правила используется для всех prerequisites, которые не появляются в качестве целевых объектов ни в одном явном правиле, и для которых не применяется неявное правило. Естественно, правило .DEFAULT не существует, пока вы его не напишете.
Если вы используете .DEFAULT без рецепта или prerequisites:
.DEFAULT:
.. то рецепт, ранее сохраненный для .DEFAULT, очищается. Тогда make действует так, как будто вы вообще никогда не определяли .DEFAULT.
Если вы не хотите, чтобы целевой объект получал рецепт из match-anything правила шаблона или из .DEFAULT, но вы также не хотите, чтобы какой-либо рецепт запускался для целевого объекта, то можете предоставить ему пустой рецепт (см. 5.9. Использование пустых рецептов).
Вы можете использовать last-resort правило, чтобы переопределить часть другого makefile. См. 3.6. Переопределение части другого Makefile.
10.7. Старомодные правила суффиксов
Правила суффиксов это старый способ определения неявных правил для make. Правила суффикса устарели, потому что правила шаблона более общие и понятные. Они поддерживаются в GNU make для совместимости со старыми файлами makefile. Правила суффиксов бывают двух видов: двойного суффикса (double-suffix) и одиночного суффикса (single-suffix).
Правило двойного суффикса определяется парой суффиксов: суффикс целевого объекта (target suffix) и суффикс исходника (source suffix). Оно совпадет с любым файлом, имя которого заканчивается на суффикс целевого объекта. Соответствующее неявное prerequisite создается заменой суффикса целевого объекта на суффикс исходника в имени файла. Правило двойного суффикса '.c.o' (у которого target и source суффиксы '.o' и '.c' соответственно) эквивалентно правилу шаблона '%.o : %.c'.
Правило одиночного суффикса определяется одним суффиксом, который является суффиксом исходника. Оно совпадет с любым именем файла, и соответствует имени неявного prerequisite, созданного путем добавления суффикса исходника. Правило одиночного суффикса, в котором суффикс исходника '.c', эквивалентен правилу шаблона '% : %.c'.
Определения правил суффикса распознаются путем сравнения каждого правила целевого объекта с определенным списком известных суффиксов. Когда make видит правило, у которого целевой объект имеет известный суффикс, это правило рассматривается как правило одиночного суффикса. Когда make видит правило, у которого целевой объект в виде двух склеенных известных суффиксов, это правило принимается в качестве правило двойного суффикса.
Например, '.c' и '.o' оба находятся с списке по умолчанию известных суффиксов. Таким образом, если вы определите правило, где target '.c.o', то make воспримет его как правило двойного суффикса, где суффикс исходника '.c' и суффикс целевого объекта '.o'. Вот пример старомодного способа определения правила для компиляции файла исходного кода языка C:
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
Правила суффикса не могут иметь собственных prerequisites. Если же они есть, то рассматриваются как обычные файлы со смешными именами, а не как правила суффикса. Таким образом, правило:
.c.o: foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
.. говорит, как создать файл .c.o из файла prerequisite foo.h, и это совсем не похоже на правило шаблона:
%.o: %.c foo.h
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
.. который говорит, как создать файлы '.o' из файлов '.c', и создает все файлы '.o', используя это правило шаблона, также зависимое от foo.h.
Правила суффикса без рецепта также бессмысленны. Они не удаляют предыдущие правила, как это делают правила шаблона без рецепта (см. 10.5.6. Отмена неявных правил). Они просто вводят в базу данных суффикс или склеенную пару суффиксов как целевой объект.
Известные суффиксы это просто имена prerequisites специальной цели .SUFFIXES. Вы можете добавить свои собственные суффиксы для .SUFFIXES, которые добавляют больше prerequisites, как в следующем примере:
.SUFFIXES: .hack .win
.. что добавит '.hack' и '.win' в конец списка суффиксов.
Если вы хотите исключить известные суффиксы по умолчанию вместо того, чтобы просто добавлять свои суффиксы, то напишите правило для .SUFFIXES без prerequisites. По специальному разрешению это удалит все существующие prerequisites цели .SUFFIXES. Вы можете затем написать другое правило, чтобы добавить суффиксы, какие захотите. Например,
.SUFFIXES: # удалит суффиксы по умолчанию
.SUFFIXES: .c .o .h # определит ваш собственный список суффиксов
Опция '-r' или '--no-builtin-rules' приведет к тому, что список суффиксов будет пустым.
Переменная SUFFIXES определена в список суффиксов по умолчанию перед тем, как make прочитает любые файлы makefile. Вы можете поменять список суффиксов через правило для специального целевого объекта .SUFFIXES, но это не изменит эту переменную.
10.8. Алгоритм поиска неявного правила
Здесь описывается процедура, используемая утилитой make при поиске неявного правила для целевого объекта t. Эта процедура выполняется для каждого правила двух двоеточий (double-colon rule) без рецепта, для каждого целевого объекта обычного правила, ни один из которых не имеет рецепта, и для каждой prerequisite, которая не является целевым объектом любого правила. Эта процедура также выполняется рекурсивно для prerequisites, которые поступают из неявных правил, в поиске цепочки правил.
Правила суффикса в этом алгоритме не упоминается, потому что правила суффикса конвертируются в эквивалентные правила шаблона после считывания файлов makefile.
Для целевых объектов элементов архива формы 'archive(member)', следующий алгоритм запустится дважды, первый раз с использованием полного имени целевого объектаt, и второй раз с использованием '(member)' в качестве целевого объекта t, если первый запуск не обнаружил правила.
1. Целевой объект t разделяется на часть директории d, и остальную часть n. Например, если t это 'src/foo.o', то d это 'src/', и n это 'foo.o'.
2. Составляется список всех правил шаблона, одно из целевых объектов которых совпало с t или n. Если шаблон целевого объекта содержит слеш, то он сопоставляется с t; иначе сопоставляется с n.
3. Если любое правило в списке не является правилом match-anything, или если t это prerequisite неявного правила, то из списка удаляются все нетерминальные match-anything правила.
4. Из списка удаляются все правила без рецепта.
5. Для каждого правила шаблона в списке:
5.1. Ищется основа (stem) s, которая представляет собой не пустую часть от t или n, совпавшую с '%' в целевом шаблоне. 5.2. Вычисляются имена prerequisite путем подстановки основы вместо '%'; если целевой шаблон не содержит слеш, то d добавляется спереди к каждому имени prerequisite. 5.3. Проверяется, существуют ли все prerequisites, или должны ли они существовать (если имя файла упоминается в makefile как целевой объект, или как явное prerequisite целевого объекта T, то мы говорим, что должна существовать). Если все prerequisites существуют или должны существовать, или нет prerequisites, то это правило применяется.
6. Если до сих пор ни одно правило шаблона не было найдено, то прикладываются дополнительные усилия. Для каждого правила шаблона в списке:
6.1. Если правило терминальное, то оно игнорируется, и происходит переход к следующему правилу. 6.2. Вычисляются имена prerequisite, как и раньше. 6.3. Проверяется, существуют ли все prerequisites, или должны ли они существовать. 6.4. Для каждого prerequisite, которое не существует, рекурсивно выполняется этот алгоритм, чтобы увидеть, может ли prerequisite быть создано неявным правилом. 6.5. Если все prerequisites существуют, должны существовать, или могут быть созданы неявными правилами, то это правило применяется.
7. Если не было найдено правило шаблона, то повторно делается попытка выполнения шагов 5 и 6 с модифицированным определением "должно существовать": если имя файла упомянуто как целевой объект или явное explicit любого целевого объекта, то этот файл должен существовать. Эта проверка присутствует только для сохранения обратной совместимости со старыми версиями GNU Make: на это поведение полагаться не рекомендуется.
8. Если ни одно неявное правило не применяется, то применяется правило для .DEFAULT, если оно присутствует. В этом случае t дается тот же рецепт, что и у .DEFAULT. Иначе для t нет рецепта.
Как только применимое правило было найдено, для каждого целевого шаблона правила, отличного от того, который соответствует t или n, символ '%' в шаблоне заменяется на s, и результирующие имя файла сохраняется до тех пор, пока не будет выполнен рецепт для пересоздания целевого файла t. После выполнения рецепта, каждый из этих сохраненных имен файлов вносится в базу данных и помечается как обновленный и имеющий такой же статус обновления, что и файл t.
Когда рецепт правила шаблона выполняется для t, автоматические переменные устанавливаются в соответствии с целевым объектом и prerequisites. См. 10.5.3. Автоматические переменные make.
[11. Использование make для обновления файлов архива (библиотеки)]
Файлы архива это файлы, содержащие внутри себя субфайлы, которые называются членами (элементами) архива; они обслуживаются программой библиотекаря (архиватора) ar, и основное назначение состоит в использовании библиотек подпрограмм для линковки.
11.1. Элементы архива в качестве целевых объектов
Индивидуальный элемент файла архива в make может использоваться в качестве целевого объекта или prerequisite. Вы укажете элемент с именем member в файле архива следующим образом:
archive(member)
Эта конструкция доступна только в целевых объектах и prerequisites, но не в рецептах! Большинство программ, которые вы можете использовать в рецептах, не поддерживают этот синтаксис, и не могут напрямую воздействовать на элементы архива. Это могут делать только ar и другие специально разработанные для поддержки библиотек программы. Таким образом, допустимые рецепты для обновления целевого объекта элемента архива вероятно должны использовать ar. Например, это правило говорит создать элемент архива hack.o в архиве foolib путем копирования в него файла hack.o:
foolib(hack.o) : hack.o
ar cr foolib hack.o
Фактически почти все целевые объекты элемента архива обновляются именно таким образом, и существует неявное правило, которое сделает это за вас. Пожалуйста имейте в виду: флаг 'c' для ar необходим, если файл архива еще не существует.
Чтобы указать несколько элементов в одном и том же архиве, вы можете записать их имена внутри круглых скобок, отделенные друг от друга пробелом. Например:
foolib(hack.o kludge.o)
Это эквивалентно следующему:
foolib(hack.o) foolib(kludge.o)
Вы можете также использовать wildcard стиля шелла в ссылках на элементы архива. См. 4.4. Использование Wildcard в именах файлов. Например, 'foolib(*.o)' расширится до всех существующих в архиве foolib элементов, имена которых оканчиваются на '.o'; вероятно для нашего примера это 'foolib(hack.o) foolib(kludge.o)'.
11.2. Неявное правило для целевых объектов элемента архива
Вспомним, что целевой объект, который выглядит наподобие a(m), означает элемент архива с именем m в файле архива a.
Когда make ищет неявное правило для такого целевого объекта, в качестве специальной фичи она рассматривает неявные правила, которые совпадут с (m), а также те, которые соответствуют фактическому целевому объекту a(m).
Это приводит к совпадению одного специального правила, у которого целевой объект (%). Это правило обновит цель a(m) путем копирования файла в архив. Например, это обновит элемент архива цели foo.a(bar.o) копированием файла bar.o в архив foo.a как элемента архива с именем bar.o.
Когда это правило выстраивается в цепочку с другими правилами, получается очень мощный результат. Таким образом 'make "foo.a(bar.o)"' (кавычки нужны для защиты '(' и ')' от специальной интерпретации шеллом) в присутствии файла bar.c достаточно, чтобы запустился следующий рецепт, даже без файла makefile:
cc -c bar.c -o bar.o
ar r foo.a bar.o
rm -f bar.o
Здесь make предусмотрела файл bar.o в качестве промежуточного файла. См. 10.4. Цепочки неявных правил.
Неявные правила, такие как это, пишутся с использование автоматической переменной '$%'. См. 10.5.3. Автоматические переменные make.
Имя элемента в архиве не может содержать имя директории, однако это может быть полезным в makefile делать вид, что это так. Если вы напишете цель элемента архива foo.a(dir/file.o), то make выполнить автоматическое обновление с этим рецептом:
ar r foo.a dir/file.o
.. который дает эффект копирования файла dir/file.o в элемент архива с именем file.o. С таким использованием могут быть полезны автоматические переменные %D и %F.
11.2.1. Обновление директорий символов архива. Файл архива, который используется в качестве библиотеки, обычно содержит в себе специальный элемент с именем __.SYMDEF, где содержится директория имен внешних символов, определенны всеми другими элементами архива. После того, как вы обновите любые другие элементы, вам нужно обновить элемент __.SYMDEF, чтобы он правильно отображал другие элементы архива. Это делается запуском программы ranlib:
ranlib archivefile
Обычно вы помещаете эту команду в правило для архивного файла, и делаете все элементы файла архива предварительными условиями (prerequisites) этого правила. Например:
libfoo.a: libfoo.a(x.o y.o …)
ranlib libfoo.a
Это даст эффект обновления элементов архива x.o, y.o, и т. д., и затем обновления элемента директории символов __.SYMDEF путем запуска ranlib. Правила для обновления элементов здесь не показаныThe rules for updating the members are not shown here; скорее всего вы можете их опустить и использовать неявное правило, которое копирует файлы в архив, как было описано в предыдущей секции.
В этом нет необходимости при использовании программы архиватора GNU ar, которая обновит элемент __.SYMDEF автоматически.
11.3. Риски при использовании архивов
Встроенные правила для обновления архивов несовместимы с параллельными сборками. Эти правила (требуемые стандартом POSIX) добавляют каждый объектный файл в архив по мере его компиляции. Когда разрешены параллельные сборки, то это разрешает нескольким командам ar одновременно обновлять один и тот же архив, что не поддерживается.
Если вы хотите использовать параллельные сборки вместе с архивами, то вы должны переопределить правила по умолчанию путем добавления в makefile таких строк:
(%) : % ;
%.a : ; $(AR) $(ARFLAGS) $@ $?
Первая строка меняет правило, которое обновляет индивидуальный объект в архиве таким образом, чтобы оно ничего не делало, а вторая строка меняет правило по умолчанию для сборки архива, чтобы обновить все устаревшие объекты ($?) в одной команде.
Конечно, вам все еще нужно декларировать prerequisites вашей библиотеки, используя синтаксис архива:
libfoo.a: libfoo.a(x.o y.o …)
Если вы предпочитаете написать явное правило, то можете использовать:
libfoo.a: libfoo.a(x.o y.o …)
$(AR) $(ARFLAGS) $@ $?
11.4. Правила суффикса для файлов архива
Вы можете написать специальный вид правила суффикса для работы с архивными файлами (библиотеками). См. 10.7. Старомодные правила суффиксов для полного описания правил суффикса. Правила суффикса архива устарели в GNU make, потому что правила шаблона для архивов предоставляют более общий механизм для того же самого (см. 11.2. Неявное правило для целевых объектов элемента архива). Однако они сохранены для совместимости с другими версиями make.
Чтобы написать правило суффикса для архива, просто напишите правило суффикса, используя целевой суффикс '.a' (обычный суффикс для файлов архива). Вот например старомодное правило суффикса для обновления архива библиотеки из файлов исходного кода C:
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
Это работает точно так же, как если бы вы написали правило шаблона:
(%.o): %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
Фактически это именно то, что делает make, когда видит правило суффикса, где указано '.a' в качестве целевого суффикса. Любое правило двойного суффикса '.x.a' конвертируется в правило шаблона с целевым шаблоном '(%.o)' и prerequisite-шаблоном '%.x'.
Поскольку вы можете захотеть использовать '.a' в качестве суффикса для какого-либо другого типа файла, make также преобразовывает правила суффикса архива в правила шаблона обычным способом (см. 10.7. Старомодные правила суффиксов). Таким образом, правило двойного суффикса '.x.a' создает два правила шаблона: '(%.o): %.x' и '%.a: %.x'.
[12. Расширение GNU make]
Утилита GNU make предоставляет множество продвинутых возможностей, включая многие полезные функции. Однако в ней не содержится полноценный язык программирования, и поэтому make имеет ограничения. Иногда эти ограничения могут быть преодолены функционалом оболочки путем вызова отдельной программы или скрипта, хотя это может быть неэффективно.
В случаях, когда встроенные возможности GNU недостаточны для ваших требований, есть две опции для расширения make. На системах, где это предоставляется, вы можете задействовать GNU Guile как встроенный язык скриптов (см. 12.1. Интеграция GNU Guile). На системах, которые поддерживают динамически загружаемые объекты, вы можете написать свое собственное расширение на любом языке программирования (которые могут компилировать код в такой объект) и загрузить его для предоставления расширенных возможностей (см. 12.2.1. Директива load).
12.1. Интеграция GNU Guile
GNU make может быть собрана с поддержкой GNU Guile в качестве встроенного языка расширения. Guile реализует язык программирования Scheme. Обзор GNU Guile, языка Scheme и его функционала выходит за рамки этого руководства, см. соответствующую документацию для GNU Guile и Scheme.
Вы можете определить, содержит ли ваша make поддержку Guile, путем проверки содержимого переменной .FEATURES; она будет содержать слово guile, если поддержка Guile доступна.
Интеграция Guile предоставляет новую make-функцию: guile. Функция guile принимает один аргумент, который расширяет make обычным способом, а затем передается вычислителю GNU Guile evaluator. Результат на выходе evaluator преобразуется в строку, и используется в makefile как расширение функции guile.
Дополнительно GNU make предоставляет Guile-процедуры для использования в скриптах Guile.
12.1.1. Преобразование типов Guile. В make существует только один "тип данных": строка. GNU Guile, с другой стороны, предоставляет богатый набор различных типов данных. Важный аспект интерфейса взаимодействия между make и GNU Guile - предобразование типов данных Guile в строки make.
Это предобразование имеет значение в двух местах: когда makefile привлекает функцию guile для вычисления Guile-выражения, результат вычисления должен быть преобразован в строку make, так чтобы она могла дальше использоваться make. И второе, когда Guile-скрипт вовлекает одну из процедур, экспортируемых утилитой make, предоставленный для процедуры аргумент должен быть преобразован в строку.
Преобразование типов Guile в make-строки выполняется следующим образом:
#f False преобразуется в пустую строку: операторы проверки условий make пустую строку рассматривают как false.
#t True преобразуется в строку '#t': операторы проверки условий make любую непустую строку рассматривают как true.
symbol number symbol или number преобразуется в строковое представление символа или числа.
character Печатаемый символ преобразуется в такой же символ.
string Строка, содержащая только печатаемые символы, преобразуется в такую же строку.
list Список преобразуется рекурсивно по описанным выше правилам. Это подразумевает, что любой структурированный список может быть сделан плоским (т. е. результат ‘'(a b (c d) e)’ будет преобразован в строку 'a b c d e').
other Преобразование любого другого типа Guile приведет к ошибке. В будущих версиях make другие типы Guile возможно смогут преобразовываться.
Трансляция '#f' (в пустую строку) и '#t' (в непустую строку '#t') разработана, чтобы позволить вам использовать двоичные результаты Guile напрямую в булевых операторах make. Например:
$(if $(guile (access? "myfile" R_OK)),$(info myfile exists))
Как следствие этих правил преобразования вы должны учитывать результат вашего скрипта Guile, так как этот результат будет преобразован в строку и подвергнут парсингу make. Если для скрипта нет натурального результата (т. е. скрипт существует исключительно для каких-то своих побочных эффектов), то вы должны добавить '#f' в качестве конечного выражения, чтобы избежать ошибки синтаксиса в вашем makefile.
12.1.2. Интерфейсы из Guile для make. В дополнение к функции guile, доступной в файлах makefile, make выставляет некоторые процедуры для использования в ваших скриптах Guile. При старте make создает новый модуль Guile, gnu make, и экспортирует эти процедуры как публичный интерфейс из этого модуля:
gmk-expand Эта процедура принимает один аргумент, который преобразуется в строку. Строка расширяется утилитой make с использованием нормальных правил расширения make. Результат расширения преобразуется в Guile-строку, и предоставляется как результат процедуры.
gmk-eval Эта процедура принимает один аргумент, который преобразуется в строку. Строка расширяется утилитой make так, как если бы это был файл makefile. Это такой же функционал, который доступен через функцию eval (см. 8.10. Функция eval). Результатом процедуры gmk-eval всегда будет пустая строка.
Обратите внимание, что gmk-eval это не то же самое, что использование gmk-expand с функцией: в последнем случае вычисленная строка будет расширена дважды; первый раз в gmk-expand, затем еще раз функцией eval.
12.1.3. Пример использования Guile в make. Ниже показан очень простой пример использования GNU Guile для управления записью в файл. Эти процедуры Guile просто откроют файл, позволяя записывать в него (по одной строке), и закроют файл. Обратите внимание, что поскольку мы не можем сохранять в переменных make сложные значения, такие как Guile-порты, мы сохранили port как глобальную переменную в интерпретаторе Guile.
Вы можете создать функции Guile, просто используя define/endef для создания скрипта Guile, затем используйте функцию guile для его встраивания:
define GUILEIO
;; Простая библиотека Guile IO для GNU Make
(define MKPORT #f)
(define (mkopen name mode)
(set! MKPORT (open-file name mode))
#f)
(define (mkwrite s)
(display s MKPORT)
(newline MKPORT)
#f)
(define (mkclose)
(close-port MKPORT)
#f)
#f
endef
# Встраивание функций Guile IO
$(guile $(GUILEIO))
Если у вас имеется значительное количество поддерживающего кода Guile, то вы можете рассмотреть его сохранение в другом файле (например, guileio.scm), и затем загрузить в свой makefile, используя функцию guile:
$(guile (load "guileio.scm"))
Достоинство этого метода в том, что при редактировании guileio.scm ваш редактор текста поймет, что это синтаксис языка Scheme, а не синтаксис makefile.
Теперь вы можете использовать эти Guile-функции для создания файлов. Предположим, что вам нужно оперировать с очень большим списком, который не может поместиться в командной строке, но используемая вами утилита также принимает список в качестве входных данных:
prog: $(PREREQS)
@$(guile (mkopen "tmp.out" "w")) \
$(foreach X,$^,$(guile (mkwrite "$(X)"))) \
$(guile (mkclose))
$(LINK) < tmp.out
Конечно, может иметь место быть более полный набор процедур манипуляций над файлами. Вы можете, например, поддерживать несколько выходных файлов одновременно, выбирая символ для каждого из них, и используя в качестве включая для хеш-таблицы, где значение является портом (port), затем возвращая символ для сохранения в переменной make.
12.2. Загрузка динамических объектов
Предупреждение: в этом релизе GNU make директива load и возможность расширения рассматривается как "технологическое превью". Разработчики make приветствуют ваши эксперименты с этой фичей и предоставление соответствующей обратной связи. Однако не гарантируется, что будет сохранена обратная совместимость в следующем релизе. Рассмотрите возможность использования GNU Guile вместо расширения GNU Make (см. 8.15. Функция guile).
Многие операционные системы предоставляют возможность динамической загрузки скомпилированных объектов. Если ваша система предоставляет такой функционал, то GNU make может его использовать для runtime-загрузки динамических объектов, предоставляя тем самым новые возможности, которые вы можете привлекать в своем makefile.
Директива load используется для загрузки динамического объекта. Как только объект загружен, вовлекается функция "setup", чтобы позволить объекту инициализировать самого себя, и зарегистрировать в GNU make новые возможности. Например, динамический объект может включать новые функции make, и функция "setup" будет регистрировать их в системе обработки функций GNU make.
12.2.1. Директива load. Объекты загружаются в GNU make путем вставки директивы load в ваш makefile. Синтаксис директивы load следующий:
load object-file ...
.. или:
load object-file(symbol-name) ...
Здесь файл object-file динамически загрузится утилитой GNU make. Если object-file не включает путь директории, то этот файл сначала ищется в текущей директории. Если он здесь не найден, или имеется путь директории, то происходит поиск в путях, специфических для системы. Если load потерпела неудачу по любой причине, то make напечатает сообщение и завершит работу.
Если load выполнилась успешно, то make привлечет функцию инициализации.
Если предоставлен symbol-name, то это будет использовано как имя функции инициализации.
Если symbol-name не предоставлен, то имя функции инициализации создается из базового имени файла object-file, до первого символа, который не относится к допустимым символам имени символа (буквенно-цифровые символы и символы подчеркивания являются допустимыми символами имени). К этому префиксу будет добавлен суффикс_gmk_setup.
Одной директивой load может быть загружено более одного объектного файла, и обе показанные выше формы аргументов load могут использоваться в той же директиве.
Функции инициализации будет предоставлено имя файла и номер строки вызова операции load. Она должна возвратить значение типа int, которое должно быть 0 при неудаче и не 0 в случае успеха. Если возвращаемое значение -1, то GNU Make попытается пересоздать объектный файл (см. 12.2.2 How Loaded Objects Are Remade).
Например:
load ../mk_funcs.so
.. загрузит динамический объект ../mk_funcs.so. После того, как объект был загружен, make привлечет функцию (предположительно определяемую shared-объектом) mk_funcs_gmk_setup.
С другой стороны:
load ../mk_funcs.so(init_mk_func)
.. загрузит динамический объект ../mk_funcs.so. После того, как объект был загружен, make привлечет функцию init_mk_func.
Независимо от того, сколько объектных файлов появляется в директиве load, они будут загружены (и будет привлечена их setup-функция) только однократно.
После того, как объектный файл был успешно загружен, его имя файла добавляется к переменной .LOADED.
Если вы предпочитаете, чтобы о сбое загрузки динамического объекта не сообщалось бы как об ошибке, то можете использовать директиву -load вместо директивы load. GNU make не будет завершаться при ошибке загрузки объектного файла, и не будет генерироваться сообщение. Отказавший объект не добавляется в переменную .LOADED, которая может быть проанализирована, чтобы проверить, была ли успешной загрузка.
12.2.2. Как пересоздаются загружаемые объекты. Загружаемые объекты подвергаются той же самой процедуре повторного создания, какая применяется для файлов makefile (см. 3.5. Как заново создаются файлы Makefile). Если любой загружаемый объект создан заново, то make запустится с чистого листа и заново загрузит все файлы makefile, и заново загрузит объектные файлы. Для поддержки этого от загружаемого объекта не требуется выполнять никаких дополнительных действий.
Автор файла makefile отвечает за предоставление правил, необходимых для пересборки загружаемого объекта.
12.2.3. Интерфейс загружаемого объекта. Предупреждение: чтобы эта фича была полезной, вашим расширениям нужно будет вызывать различные функции, внутренние по отношению к GNU make. Программные интерфейсы, предоставленные в этом релизе, не следует считать стабильными: в будущих релизах GNU make функции могут добавляться, удаляться, или у них могут поменяться сигнатуры вызова, или поменяться реализации.
Чтобы быть полезными, загружаемые объекты должны быть способны взаимодействовать с GNU make. Это взаимодействие включает в себя как интерфейсы, которые загружаемый объект предоставляет для файлов makefile, так и интерфейсы make, предоставленные для загружаемого объекта, чтобы он мог манипулировать операцией make.
Интерфейс между загруженными объектами и make определен в заголовочном файле gnumake.h языка C. Все загружаемые объекты, написанные на C, должны подключать этот файл заголовка. Любому загруженному объекту, написанному не на языке C, потребуется реализация интерфейса, определенного в этом файле заголовка.
Обычно загруженный объект будет регистрировать одну или большее количество новых функций GNU make, используя подрограмму gmk_add_function из своей setup-функции. Реализации этих make-функций могут использовать подпрограммы gmk_expand и gmk_eval для выполнения своих задач, и затем опционально возвращать строку в качестве результата расширения функции.
Лицензирование загружаемого объекта. Каждое динамическое расширение должно определять глобальный символ plugin_is_GPL_compatible, чтобы утвердить свое лицензирование под защитой GPL-совместимой лицензии. Если этот символ не существует, то make выдаст фатальную ошибку и завершит работу, когда попытается загрузить ваше расширение.
Декларируемый тип символа должен быть int. Хотя он не обязательно должен находиться в какой-либо выделенной секции. Код просто утверждает, что символ существует в глобальной области видимости. Может быть достаточно определить что-то такое:
int plugin_is_GPL_compatible;
Структуры данных. В интерфейсе расширения используется следующая структура.
gmk_floc Эта структура представляет пару имя_файла/размещение. Она предоставляется при определении элементов, так что GNU make может позже сообщить пользователю, где произошло определение, если это необходимо.
Регистрация функций. В настоящее время существует один способ для файлов makefile, чтобы вовлекать операции, предоставляемые загружаемым объектом: через интерфейс вызова функций make. Загруженный объект может зарегистрировать одну или несколько новых функций, которые могут быть затем привлечены из makefile так же, как и любая другая функция.
Для создания новой функции make используется gmk_add_function. У неё следующие аргументы:
name Имя функции. Оно должно использоваться файлом makefile для привлечения этой функции. Имя должно быть длиной от 1 до 255 символов, и может содержать только буквенно-цифровые символы, точку ('.'), тире ('-') и символ нижнего подчеркивания ('_'). Оно не должно начинаться на точку.
func_ptr Указатель на функцию, которую make будет привлекать когда расширяет функцию в makefile. Эта функция должна быть определена в загружаемом объекте.
min_args Минимальное количество аргументов, которое функция будет принимать. Должно быть значением между 0 и 255. GNU make проверит это значение, и выдаст ошибку перед вызовом func_ptr, если функция вовлекалась со слишком малым количеством аргументов.
max_args Максимальное количество аргументов, которое функция будет принимать. Должно быть значением между 0 и 255. GNU make проверит это значение, и выдаст ошибку перед вызовом func_ptr, если функция вовлекалась со слишком большим количеством аргументов. Если у max_args значение 0, то принимается любое количество аргументов. Если max_args больше 0, то оно должно быть равно или больше значения min_args.
flags Флаги, которые указывают, как эта функция будет работать; желаемые флаги должны объединяться друг с другом операцией ИЛИ. Если предоставлен флаг GMK_FUNC_NOEXPAND, то аргументы функции не будут расширены перед вызовом функции; иначе они будут предварительно расширены.
Интерфейс зарегистрированной функции. Зарегистрированная с make функция должна соответствовать типу gmk_func_ptr. Она будет вовлекаться с тремя параметрами: name (имя функции), argc (количество аргументов функции) и argv (массив указателей на аргументы функции). Последний указатель (т. е. argv[argc]) будет null (0).
Возвращаемое значение функции это результат расширения функции. Если функция расширяется в ничего, то её возвращаемое значение может быть null. Иначе это должен быть указатель на строку, созданную с помощью gmk_alloc. Как только функция выполнила возврат, make вступает во владение этой строкой, и освободит её, когда это будет нужно; к ней не может обращаться загруженный объект.
Объекты GNU make. Есть несколько объектов (facilities), экспортируемых утилитой GNU make для использования загруженными объектами. Обычно они запускаются из setup-функции и/или функций, зарегистрированных функцией gmk_add_function, для извлечения или модификации данных, с которыми работает make.
gmk_expand Эта функция принимает строку и расширяет её, используя правила расширения make. Результат расширения возвращается в виде буфера строки, завершающейся 0. Вызывающий код отвечает за вызов gmk_free с указателем на этот возвращенный буфер, когда работа с содержимым буфера завершена.
gmk_eval Эта функция принимает буфер, и обрабатывает его как сегмент синтаксиса makefile. Эта функция может быть использована для определения новых переменных, новых правил и т. д. Это эквивалентно использованию make-функции eval.
Обратите внимание, что есть различие между gmk_eval и вызовом gmk_expand со строкой, используя функцию eval: в последнем случае строка будет расширена дважды; один раз в gmk_expand, и затем еще раз функцией eval. При использовании gmk_eval буфер расширяется только однократно (когда считывается парсером make).
Управление памятью. Некоторые системы позволяют использовать разные схемы управления памятью. Таким образом, вы не должны передавать память, которую выделили непосредственно для любой функции make, и не должны пытаться напрямую освобождать любую память, возвращенную вам любой функцией make. Вместо этого используйте функции gmk_alloc и gmk_free.
В частности строка, возвращенная для make функцией, зарегистрированной с помощью gmk_add_function, должна быть выделена с помощью gmk_alloc, и строка, возвращенная из make функцией gmk_expand, должна быть освобождена (когда она больше не нужна), с помощью gmk_free.
gmk_alloc Возвратит указатель на новый выделенный буфер. Эта функция всегда возвратит допустимый указатель; если недостаточно памяти для выделения, то make завершит работу. Функция gmk_alloc не инициализирует выделенную память.
gmk_free Освободит буфер, возвращенный вам утилитой make. Как только функция gmk_free выполнила возврат, содержимое буфера больше не является достоверным. Если в gmk_free передано NULL, то никакое действие не производится.
12.2.4. Пример загружаемого объекта. Предположим, что мы хотим написать новую функцию GNU make, которая будет создавать временный файл и возвращать его имя. Мы хотим, чтобы наша функция брала префикс в качестве аргумента. Сначала мы можем написать эту функцию на языке C в файле mk_temp.c:
#include < stdlib.h> #include < stdio.h> #include < string.h> #include < unistd.h> #include < errno.h> #include < gnumake.h>
int plugin_is_GPL_compatible;
char *gen_tmpfile(const char *nm, int argc, char **argv)
{
int fd;
/* Вычислим размер имени файла и выделим под него память */
int len = strlen (argv[0]) + 6 + 1;
char *buf = gmk_alloc (len);
strcpy (buf, argv[0]);
strcat (buf, "XXXXXX");
fd = mkstemp(buf);
if (fd >= 0)
{
/* Не допускайте утечки памяти на дескрипторе файла. */
close (fd);
return buf;
}
/* Ошибка. */
fprintf (stderr, "mkstemp(%s) failed: %s\n", buf, strerror (errno));
gmk_free (buf);
return NULL;
}
int mk_temp_gmk_setup (const gmk_floc *floc)
{
printf ("mk_temp plugin loaded from %s:%lu\n", floc->filenm, floc->lineno);
/* Регистрация функции под make-именем "mk-temp". */
gmk_add_function ("mk-temp", gen_tmpfile, 1, 1, 1);
return 1;
}
Затем мы напишем Makefile, который будет выполнять сборку этого shared-объекта, загружать его, и использовать:
all:
@echo Temporary file: $(mk-temp tmpfile.)
load mk_temp.so
mk_temp.so: mk_temp.c
$(CC) -shared -fPIC -o $@ $<
На MS-Windows из-за особенностей того, как создаются shared-объекты, компилятору необходимо сканировать библиотеку импорта, создаваемую при сборке make, обычно называемую libgnumake-version.dll.a, где version это версия load object API. Таким образом, рецепт для создания shared-объекта на Windows будет выглядеть следующим образом (подразумевается, что используется API версии 1):
mk_temp.dll: mk_temp.c
$(CC) -shared -o $@ $< -lgnumake-1
Теперь, когда вы запустите make вы увидите что-то наподобие следующего:
$ make
mk_temp plugin loaded from Makefile:4
cc -shared -fPIC -o mk_temp.so mk_temp.c
Temporary filename: tmpfile.A7JEwd
[Ссылки]
1. GNU make. 2. Руководство GNU make: главы 1-3. |