Вперед Назад Содержание

8. Функции преобразования текста

Функции позволяют вам производить в make-файле обработку текста для определения обрабатываемых файлов или используемых команд. Вы используете функцию при помощи вызова функции, где вы указываете имя функции и определенный текст (аргументы), который предназначен для обрабоки с помощью функции. Результат работы функции подсталяется в make-файл на место вызова, точно также, как могло быть подставлено значение переменной на место ссылки на нее.

8.1 Синтаксис вызова функции

Вызов функции внешне напоминает ссылку на переменную. Он выглядит так:

$(ФУНКЦИЯ АРГУМЕНТЫ)
или так:

${ФУНКЦИЯ АРГУМЕНТЫ}
Здесь ФУНКЦИЯ представляет собой имя функции, которое берется из небольшого набора имен, встроенных в программу make. Для определения новых функций возможностей нет.

АРГУМЕНТЫ представляют собой аргументы функции. Они отделяются от имени функции одним или более пробелами или символами табуляции, а в том случае, если имеется более, чем один аргумент, то они разделяются запятыми. Эти пробельные символы и запятые не являются частью значения аргумента. Ограничители, используемые вами для ограничения вызова функции, как круглые скобки, так и фигурные, могут появляться среди аргументов только с соответствующими парными символами, другие виды ограничителей могут появляться в одиночку. Если аргументы сами содержат ссылки вызовы других функций или ссылки на переменные, правильнее всего использовать один и тот же вид ограничителей для всех ссылок - то есть, пишите '$(subst a,b,$(x))', а не '$(subst a,b,${x})'. Причина в том что запись с одним видом ограничителей является более ясной, а также, при такой записи, для нахождения конца ссылки ищется только один вид ограничителей.

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

Запятые и непарные скобки, круглые или квадратные, не могут явным образом появляться в тексте, соответствующем аргументу, ведущие пробелы не могут явным образом появляться в тексте, соответствующем первому аргументу. Эти символы могут быть помещены в значение аргумента путем подстановки переменной. Сначала надо определить переменные comma и space, значениями которых являются отдельные символы запятой и пробела, а затем подставить эти переменные там, где требуются такие символы, как показано ниже:

comma:= , empty:= space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo)) # bar is now 'a,b,c'.
В этом примере функция subst заменяет каждый пробел вместо запятой во всем значении переменной foo, после чего результат работы функции подставляется на место ее вызова.

8.2 Функции подстановки и анализа строк

Ниже приведены некоторые функции, которые обрабатывают строки:

'$(subst ФРАГМЕНТ,ЗАМЕНА,ТЕКСТ)'

Выполняет текстуальную замену в тексте ТЕКСТ: каждое вхождение ФРАГМЕНТА заменяется на ЗАМЕНУ. Результат подставляется на место вызова функции. Например, на место следующего вызова функции:

$(subst ee,EE,feet on the street)
подставляется строка 'fEEt on the strEEt'.

'$(patsubst ШАБЛОН,ЗАМЕНА,ТЕКСТ)'

Находит в ТЕКСТЕ разделенные пробельными символами слова, соответствующие ШАБЛОНУ, и заменяет их на ЗАМЕНУ. При этом шаблон может содержать символ '%', который действует как шаблон, соответствующий любому количеству любых символов внутри слова. Если в ЗАМЕНЕ также содержатся символы '%', то они заменяются текстом, соответствующим символу '%' в шаблоне.

Специальное значение символа '%' в вызове функции patsubst может быть отключено предшествующим символом '\'.Специальное назначение символа '\', который в противном случае отменял бы специальное назначение последующего символа '%', может быть отменено еще одним символом '\'. Символы '\', отменяющие специальное назначение символов '%' или других символов '\', удаляются из шаблона перед тем, как он будет сравниваться с именами файлов или в него будет подставляться основа. Символы '\', которые заведемо не влияют на трактовку символа '%', остаются нетронутыми. Например, в шаблоне 'the\%weird\\%pattern\\' фрагмент 'the%weird\' предшествует действующему символу '%', а фрагмент 'pattern\\' следует за ним. С двумя заключительными символами '\' ничего не происходит, поскольку они не могут воздействовать ни на какие символы '%'.

Пробельные символы между словами преобразуются в одиночные пробелы, ведущие и ведомые пробельные символы отбрасываются.

Например, следующий вызов функции

$(patsubst %.c,%.o,x.c.c bar.c)
порождает значение 'x.c.o bar.o'.

Ссылки с заменой (смотрите раздел 6.3.1 [Cсылки с заменой]) являются более простым способом получить результат, аналогичный использованию функции patsubst. Например, такая ссылка с заменой:

$(VAR:PATTERN=REPLACEMENT)
эквивалентна вызову функции patsubst:

$(patsubst PATTERN,REPLACEMENT,$(VAR))
Еще одно сокращение упрощает одно из наиболее частых использований функции patsubst: замену суффикса в конце именем файлов. Такая ссылка с заменой:

$(VAR:SUFFIX=REPLACEMENT)
эквивалентна вызову функции patsubst:

$(patsubst %SUFFIX,%REPLACEMENT,$(VAR))
Например, у вас мог бы быть список объектных файлов:

objects = foo.o bar.o baz.o
Чтобы получить список соответствующих исходных файлов, вы могли

бы просто написать:

$(objects:.o=.c)

вместо испрользования общей формы:

$(patsubst %.o,%.c,$(objects))
'$(strip СТРОКА)'

Удаляет ведущие и ведомые пробелы из СТРОКИ заменяет каждую внутреннюю последовательность из одного или более пробельного символа на один пробел. Таким образом, результатом вызова '$(strip a b c )' является 'a b c'.

Функция strip может быть очень полезной при использовании ее вместе с условными конструкциями. При сравнении чего-либо с пустой строкой '' с помощью директив ifeq или ifneq вы обычно хотите, чтобы строка, состоящая только из пробельных символов, была равна пустой строке (смотрите главу 7 [Условные конструкции]).

Таким образом, в следующем фрагменте make-файла, возможно, не удастся получить желаемый результат:

.PHONY: all ifneq "$(needs_made)" "" all: $(needs_made) else all:;<htmlurl name="@echo" url="mailto:@echo"> 'Nothing to make!' endif
Замена в директиве ссылки на переменную $(needs_made) на вызов функции $(strip $(needs_made)) ifneq сделало бы этот фрагмент более корректным.

'$(findstring ФРАГМЕНТ,СТРОКА)'

Ищет в СТРОКЕ вхождение ФРАГМЕНТА. Если вхождение есть, то результатом функции является ФРАГМЕНТ, в противном случае результатом является пустая строка. Вы можете использовать эту функцию в условной конструкции для того, чтобы проверить наличие специальной подстроки в данной строке. Таким образом, следующих вызовы функций:

$(findstring a,a b c) $(findstring a,b c)
порождают, соответственно, значения 'a' и '' (пустую строку). Смотрите раздел 7.3 [Проверка опций] для информации опрактическом применении функции findstring.

'$(filter ШАБЛОН...,ТЕКСТ)'

Удаляет из ТЕКСТА все разделенные пробельными символами слова, которые не соответствуют ни одному из шаблонных слов, возвращая только слова, соответствующие, по крайней мере, одному из шаблонов. Шаблоны пишутся с использованием символа '%', также как и шаблоны, используемые в описанной выше функции patsubst.

Функция filter может быть использована для выделения из переменной различных типов строк (таких, как имена файлов). Например, приведенный ниже фрагмент make-файла:

sources := foo.c bar.c baz.s ugh.h foo: $(sources) cc $(filter %.c %.s,$(sources)) -o foo
говорит о том, что файл 'foo' зависит от файлов 'foo.c', 'bar.c', 'baz.s' и 'ugh.h', но только 'foo.c', 'bar.c' и 'baz.s' должны быть определены для компилятора в командной строке.

'$(filter-out ШАБЛОН...,ТЕКСТ)'

Удаляет из ТЕКСТА все разделенные пробельными символами слова, которые соответствуют какому-нибудь из шаблонных слов, возвращая только слова, не соответствующие ни одному из шаблонов. Это является точной противоположностью функции filter.

Например, при таких определениях переменных:

objects=main1.o foo.o main2.o bar.o mains=main1.o main2.o
приведенный ниже фрагмент make-файла приводит к генерации списка, содержащего все объектные файлы, не указанные в переменной mains

$(filter-out $(mains),$(objects))

'$(sort СПИСОК)'

Список в лексическом порядке слова из СПИСКА, удаляя дублирующиеся слова. Результатом является список слов, разделенных одиночными пробелами. Таким образом, при таком вызове функции:

$(sort foo bar lose)
получается значение 'bar foo lose'.

Кстати, поскольку функция sort удаляет дублирующиеся слова, вы можете использовать ее для этой цели, даже если вам не нужны возможности, связанные с сортировкой.

Вот реалистичный пример использования функций subst и patsubst. Предположим, что make-файл использует переменную VPATH для определения списка каталогов, в которых программа make должна искать файлы зависимости (смотрите раздел 4.3.1 [VPATH: Путь поиска для всех зависимостей]). Это пример показывает, как указать C-компилятору искать заголовочные файлы в том же списке каталогов.

Значение переменной VPATH представляет собой список каталогов, разделенных двоеточиями, например 'src:../headers'. Сначала надо использовать функцию subst для замены двоеточий на пробелы:

$(subst :, ,$(VPATH))
В результате получается значение 'src ../headers'. Затем следует использовать функцию patsubst для того чтобы подставить перед каждым именем каталога оицию '-I'. Получившееся значение может быть добавлено к значению переменной CFLAGS, которая автоматически передается C-компилятору, как показано ниже:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))
В итоге к имевшемуся ранее значению переменной CFLAGS добавляется фрагмент '-Isrc -I../headers'. Из-за использования директивы override, присваивание нового значения будет происходить даже в том случае, если предыдущее значение переменной CFLAGS было определено при помощи аргумента командной строки.

8.3 Функции для обработки имен файлов

Несколько дополнительных встроенных функций специально ориентированы на работу с именами файлов и списками имен файлов.

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

'$(dir ИМЕНА...)'

Выделяет часть, определяющую каталог, из каждого имени файла, указанного в списке ИМЕН. Часть имени файла, определяющая каталог, представляет собой часть имени от его начала до последнего символа '/' (включительно). Если в имени файла не содержится символа '/', частью, определяющей каталог, является './'. Например, при таком вызове функции:

$(dir src/foo.c hacks)
в качестве результата получается 'src/ ./'.

'$(notdir ИМЕНА...)'

Выделяет из каждого имени файла, указанного в списке ИМЕН, то, что не входит в часть, определяющую каталог,. Если имя файла не содержит ни одного символа '/', то оно остается неизмененным. В противном случае, из имени файла удаляется все то, что в нем расположено до последнего символа '/'.

Имя файла, заканчивающееся символом '/' преобразуется в пустую строку. Это является удачным, поскольку означает, что результат не всегда содержит такое же количество разделенных пробельными символами имен файлов, как и аргумент, но мы не видим другой подходящей альтернативы. Например, при таком вызове функции:

$(notdir src/foo.c hacks)
в качестве результата получается 'foo.c hacks'.

'$(suffix ИМЕНА...)'

Выделяет суффикс каждого имени файла из списка ИМЕН. Если имя файла содержит точку, то суффиксом является часть имени от его последней точки до конца. В противном случае, суффиксом является пустая строка. Часто это означает, что результат функции будет пустым при непустом аргументе, а при аргументе, содержащем несколько имен файлов, результат может содержать их в меньшем количестве:

Например, при таком вызове функции:

$(suffix src/foo.c hacks)
в качестве результата получается '.c'.

'$(basename ИМЕНА...)'

Выделяет из каждого имени файла из списка ИМЕН базовое имя - все то, что не относится к суффиксу. Если имя файла содержит точку, то базовым именем является часть имени от его начала последней точки (исключительно). В противном случае, базовым именем является все имя файла. Например, при таком вызове функции:

$(basename src/foo.c hacks)
в качестве результата получается 'src/foo hacks'.

'$(addsuffix СУФФИКС,ИМЕНА...)'

Аргумент ИМЕНА рассматривается как последовательность имен, разделенных пробельными символами, а СУФФИКС используется как одно целое. Значение аргумента СУФФИКС добавляется в конец каждого отдельного имени и получившиеся удлиненные имена сцепляются, с одиночными пробелами между собой. Например, при таком вызове функции:

$(addsuffix .c,foo bar)

в качестве результата получается 'foo.c bar.c'.

'$(addprefix ПРЕФИКС,ИМЕНА...)'

Аргумент ИМЕНА рассматривается как последовательность имен, разделенных пробельными символами, а ПРЕФИКС используется как одно целое. Значение аргумента ПРЕФИКС двставляется в начало каждого отдельного имени и получившиеся удлиненные имена сцепляются, с одиночными пробелами между собой. Например, при таком вызове функции:

$(addprefix src/,foo bar)
в качестве результата получается 'src/foo src/bar'.

'$(join СПИСОК1,СПИСОК2)'

Сцепляет слова из двух аргументов: два первых слова (по одному из каждого аргумента), в результате сцепления, образуют первое слово результата, два вторых слова образуют второе слово результата, и так далее. Таким образом, n-е слово результата образуется из n-х слов каждого аргумента. Если в одном из аргументов слов больше, чем в другом, избыточные слова копируются в результат неизмененными.

Например, при вызове '$(join a b,.c .o)' в качестве результата получается 'a.c b.o'.

Пробельные символы между словами в списке не сохраняются - они заменяются одиночными пробелами.

Эта функция может слить результаты функций dir and notdir, порождая первоначальный список файлов, переданный этим двум функциям.

'$(word N,ТЕКСТ)'

Возвращает N-е слово ТЕКСТА. Допустимые значения переменной N начинаются с 1. Если N больше, чем количество слов в ТЕКСТЕ, результатом является пустое значение. Например, при таком вызове:

$(word 2, foo bar baz)
результатом будет 'bar'.

'$(words ТЕКСТ)'

Возвращает количество слов в ТЕКСТЕ. Таким образом, последнее слово текста может быть получено при помощи вызова '$(word $(words TEXT),TEXT)'.

'$(firstword ИМЕНА...)'

Аргумент ИМЕНА рассматривается как последовательность имен, разделенных пробельными символами. Значением является первое имя в последовательности. Оставшаяся часть имени игнорируется.

Например, при таком вызове функции:

$(suffix src/foo.c hacks)
в качестве результата получается 'foo'. Хотя вызов $(firstword TEXT) аналогичен вызову $(word 1,TEXT), функция firstword остается в употреблении из-за ее простоты.

'$(wildcard ШАБЛОН)'

Аргумент ШАБЛОН является шаблоном имени файла, обычно содержащим шаблонные символы (как шаблонах имени файла, используемых в командной оболочке). Результатом функции wildcard является разделенный пробелами список имен существующих файлов, удовлетворяющих шаблону. Смотрите раздел 4.2 [Использование шаблонных символов в именах файлов].

8.4 8.4 Функция foreach

Функция foreach сильно отличается о других функций. При ее использовании определенная часть текста используется повторно, при этом каждый раз над ней выполняются различные подстановки. Это похоже на команду for в командной оболочке sh и на команду csh в командной C-оболочке csh.

Синтакисис функции foreach слудующий:

$(foreach ПЕРЕМЕННАЯ,СПИСОК,ТЕКСТ)
Первые два аргумента, ПЕРЕМЕННАЯ и СПИСОК, вычисляются до того, как что-либо еще будет сделано; обратите внимание, что последний аргумент, ТЕКСТ, не вычисляется в это время. Затем для каждого слова из вычисленного значения аргумента СПИСОК, переменная с именем, полученным из вычисленного значения аргумента ПЕРЕМЕННАЯ, получает в качестве значения это слово, и аргумент ТЕКСТ вычисляется. Предполагается, что ТЕКСТ содержит ссылки на эту переменную, поэтому результат ее вычисления будет каждый раз различным.

В итоге аргумент ТЕКСТ вычисляется столько раз, сколько разделенных пробельными симвролами слов есть в СПИСКЕ. Результаты множественных вычислений аргумента ТЕКСТ сцепляются, с пробелами между ними, порождая результат функции foreach.

В приведенном ниже простом примере в качестве значения переменной 'files' устанавливается список всех файлов в каталогах, перечисленных списке, представленном переменной 'dirs':

dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
В данном случае значением аргумента является '$(wildcard $(dir)/*)'. При первой итерации в качестве значения dir берется 'a', что приводит к такому же результату, как и вызов '$(wildcard a/*)', при второй итерации получается результат, аналогичный вызову '$(wildcard b/*)', а при третьей - вызову '$(wildcard c/*)'.

Этот пример дает такой же результат (за исключением установки переменной dirs), как и следующий пример:

files := $(wildcard a/* b/* c/* d/*)

Когда аргумент ТЕКСТ сложен, вы можете улучшить читабельность, дав ему имя при помощи дополнительной переменной:

find_files = $(wildcard $(dir)/*) dirs := a b c d files := $(foreach dir,$(dirs),$(find_files))
В этом примере мы для этого используем переменную find_files. Мы используем просто символ '=' для того, чтобы определить рекурсивно-вычисляемую переменную, и поэтому ее значение, на самом деле, содержит вызов функции, который повторно вычисляется под управлением функции foreach - для упрощенно-вычисляемой переменной это бы не сработало, так как функция wildcard была бы вызвана только один раз, во время определения переменной find_files.

Функция foreach не оказывает необратимого эффекта на переменную, соответствующую аргументу ПЕРЕМЕННАЯ - ее значение и разновидность после вызова функции foreach остаются такими же, как и были ранее. Другие значения, которые берутся из СПИСКА, находятся в действии только временно, в период выполнения функции foreach. Переменная, соответствующая аргументу ПЕРЕМЕННАЯ, в период выполнения функции foreach является упрощенно вычисляемой переменной. Если переменная, соответствующая аргументу ПЕРЕМЕННАЯ, была неопределенной перед вызовом функции foreach, то она остается неопределенной и после вызова. Смотрите раздел 6.2 [Две разновидности переменных].

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

files := $(foreach Esta escrito en espanol!,b c ch,$(find_files))
могло быть полезным, если бы в значении переменной find_files была ссылка на переменную с именем 'Esta escrito en espanol!', но наиболее вероятно, что это является ошибкой.

8.5 Функция origin

Функция origin отличается от большинства других функций тем, что она не обрабатывает значение переменной - она дает вам определенную информацию о переменной. В частности, она дает вам информацию о происхождении переменной.

Функция origin имеет такой синтаксис:

$(origin ПЕРЕМЕННАЯ)
Обратите внимание на то, что аргумент ПЕРЕМЕННАЯ - это имя переменной, на которую делается запрос, а не ссылка на эту переменную. Следовательно, обычно при написании аргумента вы не будете использовать символ '$' или круглые скобки. (Однако, вы можете использовать в имени ссылку на переменную, если хотите, чтобы имя не было фиксированным.)

Результатом этой функции является строка, дающая вам информацию о том, как была определена переменная, определяемая аргументом ПЕРЕМЕННАЯ:

'undefined'

если эта переменная нигде не была определена.

'default'

Если эта переменная имеет определение, действующее по умолчанию, что обычно имеет место для переменной CC и подобных ей. Смотрите раздел 10.3 [Переменные, используемые неявными правилами]. Обратите внимание, что если вы переопределили переменную, имеющую значение по умолчанию, функция origin даст вам информацию о происхождении, соответствующую более позднему определению.

'environment'

Если эта переменная была определена как переменная командной среды, и при этом не указана опция '-e' (смотрите раздел 9.7 [Обзор опций]).

'environment override'

Если эта переменная была определена как переменная командной среды, и при этом указана опция '-e' (смотрите раздел 9.7 [Обзор опций]).

'file'

Если эта переменная была определена в make-файле

'command line'

Если эта переменная была определена в командной строке

'override'

Если эта переменная была определена в make-файле при помощи директивы override (смотрите раздел 6.7 [Директива override]).

'automatic'

Если эта переменная является автоматической переменной, определяемой для выполнения команд в каждом правиле (смотрите раздел 10.5.3 [Автоматические переменные]).

Эта информация полезна в первую очередь (не считая вашего любопытства) для определения, хотите ли вы доверять значению переменной. Например, предположим, что у вас есть make-файл 'foo', в котором происходит включение другого make-файла - 'bar'. Вы хотите, чтобы при использовании команды 'make -f bar' переменная bletch была определена в файле 'bar', даже если в командной среде содержится определение переменной bletch. Однако, если в файле 'foo' перед включением файла 'bar' определена если переменная bletch, вы не хотите перекрывать это определение. Это могло бы быть реализовано с использованием в файле 'foo' директивы override, что давало бы этому определению преимущество перед более поздним определением в фале 'bar', но, к сожалению, директива override перекрыла бы также любые определения, данные в командной строке. Таким образом, файл 'bar' мог бы содержать такой фрагмент:

ifdef bletch ifeq "$(origin bletch)" "environment" bletch = barf, gag, etc. endif endif
Если бы переменная bletch была определена в командной среде, это вызвало бы ее переопределение.

Если вы хотите перекрыть предыдущее определение переменной bletch, если она определена в командной среде, даже при использовании опции '-e', вы могли бы вместо этого написать:

ifneq "$(findstring environment,$(origin bletch))" "" bletch = barf, gag, etc. endif
В данном случае переопределение имеет местов том случае, если при вызове '$(origin bletch)' возвращается значение 'environment' или 'environment override'. Смотрите раздел 8.2 [Функции подстановки и анализа строк].

8.6 Функция shell

Функция shell отличается от любой другой функции, кроме функции wildcard (смотрите раздекл 4.2.3 [Функция wildcard]), тем, что она общается с внешним, по отношению к программе make, миром.

Функция shell выполняет те же действия, которые выполняют обратные апострофы ('`') в большинстве командных оболочек: она выполняет подстановку результатов команд. Это означает, что она принимает аргумент, являющийся командой командной оболочки, а ее результатом является выход этой команды. Единственной работой, выполняемой программой make над результатом перед подстановкой его в окружающий текст, является преобразование символов перевода строки в пробелы.

Команды, запускаемые при вызовах функции shell, запускаются в момент выполнения вызовов функции. В большинстве случаев, это происходит в момент считывания make-файла. Исключение состоит в том, что вызовы функции в командах правила выполняются в момент запуска команд, и это правило, точно так же, как и к другим, применимо и к функции shell.

Ниже приведены некоторые примеры использования функции shell. В первом случае:

contents := $(shell cat foo)
в качестве значения переменной contents устанавливается содержимое файла 'foo', с пробелом (а не символом перевода строки) в качестве разделителей строк файла. А во втором случае:

files := $(shell echo *.c)
в качестве значения переменной contents устанавливается результат поиска файлов текущего каталога, соответствующих шаблону '*.c'. Если программа make не использует очень необычную команду оболочки, такой вызов дает такой же результат, что и вызов '$(wildcard *.c)'.


Вперед Назад Содержание