Программирование ARM Makefile: установка переменных Sat, May 04 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Makefile: установка переменных Печать
Добавил(а) microsin   

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

objects = main.o foo.o bar.o utils.o

определяет переменную с именем objects, которая содержит значение 'main.o foo.o bar.o utils.o'. Пробелы вокруг имени переменной и сразу после '=' игнорируются.

Переменные, определенные через '=', являются рекурсивно развернутыми (recursively expanded variables). Переменные, определенные через ':=' или '::=' являются просто расширенными (simply expanded variables); эти определения могут содержать ссылки на переменные, которые будут расширены до создания определения. Переменные, определенные через ':::=', расширяются немедленно.

Существует несколько способов, которыми в GNU make переменная может получить свое значение. Эти способы различаются тем, как они обрабатывают значения, которые им присваиваются в makefile, и как эти значения управляются, когда позже переменная используется или расширяется.

[Recursively Expanded Variable Assignment]

Первый способ это рекурсивно расширяемая переменная (recursively expanded variable). Переменные такого сорта определяются через ‘=’ или директивой define (см. описание определения многострочных переменных [7]). Указанное значение устанавливается дословно; если определение содержит ссылки на другие переменные, то эти ссылки расширяются всякий раз, когда эта переменная подставляется (в процессе расширения другой строки). Когда такое происходит, это называется рекурсивным расширением.

Например:

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

Здесь echo 'Huh?': '$(foo)' расширится в '$(bar)' что расширяется в '$(ugh)', что в конечном счете расширяется в 'Huh?'.

Этот вид переменной единственный, поддерживаемый большинством версий make. Он имеет свои преимущества и недостатки. Достоинство (как многие считают) в следующем:

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar

Это будет делать то, что задумано: когда ‘CFLAGS’ расширен в источнике, он расширяется в '-Ifoo -Ibar -O'. Основным недостатком является то, что вы не можете добавить что-то в конце переменной, как в следующем примере:

CFLAGS = $(CFLAGS) -O

Потому что это приведет к бесконечному циклу при расширении переменной (фактически это будет распознано, и выведено сообщение об ошибке).

Другой недостаток в том, что любые функции (см. [8]), на которые есть ссылка в определении, будут выполнены каждый раз, когда переменная расширяется. Это замедляет работу make; еще хуже, что wildcard и функции shell дают непредсказуемые результаты, потому что нет простого способа управлять, когда они будут вызываны, или насколько часто это произойдет.

[Simply Expanded Variable Assignment]

Чтобы избежать проблем неудобств рекурсивно расширяемых переменных, был введен другой их вариант: просто расширяемые переменнные (simply expanded variables).

Они определяются через ':=' или '::='. В GNU make эти формы эквивалентны; однако в стандарте POSIX описана только форма '::=' (поддержка '::=' была добавлена в стандарт для POSIX Issue 8).

Значение просто расширяемой переменной сканируется один раз, расширяя любые ссылки на другие переменные и функции, когда эта переменная определена. Как только расширение завершено, значение этой переменной больше никогда не расширяется: когда переменная используется, её значение копируется дословно в качестве расширения. Если значение содержит ссылки на переменные, то результат расширения будет содержать результат расширения на момент определения этой переменной. Поэтому

x := foo
y := $(x) bar
x := later

эквивалентно следующему:

y := foo bar
x := later

Вот несколько более сложный пример, иллюстрирующий использование ':=' совместно с функцией shell (см. описание функции shell [6]). Этот пример также показывает использование переменной MAKELEVEL, которая изменяется, когда происходит переход с одного уровня на другой (см. [9] для информации по MAKELEVEL).

ifeq (0,${MAKELEVEL})
whoami    := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif

Достоинство этого использования ':=' в том, что тогда типовой рецепт "пуска в директорию" выглядит так:

${subdirs}:
        ${MAKE} -C $@ all

Просто расширяемые переменные обычно делают сложное программирование makefile для make более предсказуемым, потому что эти переменные работают так же, как переменные в большинстве языков программирования. Они позволяют вам переопределить переменную, используя её собственное значение (или её значение, обработанное каким-либо способом одной из функций расширения), и использовать функции расширения гораздо более эффективно (см. [8]).

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

nullstring :=
space := $(nullstring) # конец строки

Здесь значение переменной space содержит ровно 1 пробел. Комментарий "# конец строки" здесь добавлен только для ясности. Поскольку завершающие символы пробела не вырезаются из значений переменных, простой пробел в конце строки будет давать тот же эффект (однако это будет трудно читать). Если вы поместите пробел в конец значения переменной, то хорошей идеей будет поместить такой комментарий в конец строки, чтобы пояснить свои намерения. И наоборот, если в конце строки не требуется вводить символы пробела, то вы должны помнить, что не нужно помещать случайный комментарий в конецт строки после пробела, как здесь:

dir := /foo/bar    # директория для размещения лягушек

Здесь значение переменной dir будет "/foo/bar    " (с 4 завершающими пробелами), что вероятно не то, что нужно было получить (представьте себе что-нибудь наподобие $(dir)/file в этом определении!).

[Immediately Expanded Variable Assignment]

Эта форма назначения позволяет немедленное расширение при назначении переменной (immediate expansion), однако в отличие от простого назначения (simple assignment) результирующая переменная рекурсивная: она будет заново расширена снова при каждом использовании. Чтобы избежать неожиданных результатов, после немедленного расширения значение будет автоматически закрываться в кавычки: все экземпляры $ в значении после расширения будут преобразованы в $$. Этот тип назначения использует оператор ':::='. Например,

var = first
OUT :::= $(var)
var = second

в результате присвоит переменной OUT текст 'first', в то время как здесь:

var = one$$two
OUT :::= $(var)
var = three$$four

результат для переменной OUT будет содержать текст 'one$$two'. Значение расширяеься при назначении переменной, поэтому результатом являетсмя расширение первого значения var, 'one$two'; затем значение повторно ообрабатывается перед присваиванием, давая конечный результат'one$$two'.

После этого переменная OUT считается рекурсивной, поэтому при её использовании она будет повторно расширена.

Это выглядит функционально эквивалентным операторам ':=' / '::=', но с некоторыми отличиями:

• Во-первых, после назначения переменная является нормальной рекурсивной переменной; при добавлении к ней '+=' значение в правой части не будет расширено немедленно. Если вы предпочитаете, чтобы оператор '+=' немедленно расширял правую сторону, вместо этого следует использовать назначеие ':=' / '::='.

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

Вот другой пример:

var = one$$twoOUT :::= $(var)OUT += $(var)var = three$$four

После этого значение OUT это текст "one$$two $(var)". Когда эта переменная исопльзуется, она будет расширена, и результат получится "one$two three$four".

Этот стиль назначения эквивалентен традиционному BSD make оператору ':='; как вы можете видеть, это работает несколько иначе, чем GNU make оператор ':='. Оператор :::= был добавлен в спецификацию для POSIX Issue 8, чтобы обеспечить портируемость.

[Conditional Variable Assignment]

Существует другой опеартор назначения переменных '?='. Он называется условным оператором присваивания, потому что срабатывает только если переменная еще не была определена. Этот оператор:

FOO ?= bar

полностью эквивалентен следующему (см. описание функции origin [5]):

ifeq ($(origin FOO), undefined)
  FOO = bar
endif

Обратите внимание, что переменная, имеющая пустое значение, по-прежнему считается определенной, поэтому для неё '?=' не установит значение.

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

Не существует предела для длины значения переменной, кроме количество памяти в компьютере. Для читаемости вы можете разделить переменную не несколько физических строк [2].

Большинство имен переменных считаются пустой строкой в качестве значения, если они никогда не задавались. Некоторые переменные имеют встроенные начальные значения которые не являются пустыми, но их можно задать обычными способами (см. [3]). Некоторые специальные переменные устанавливаются автоматически на новое значение для каждого правила; такие переменные называются автоматическими (см. [4]).

Если переменная должна быть установлена в значение только в том случае, если она еще не задана, то можно использовать сокращенный оператор '?=' вместо '='. Эти следующие два варианта установки переменной 'FOO' эквивалентны (см. описание функции origin [5]).

FOO ?= bar

То же самое, что и:

ifeq ($(origin FOO), undefined)
FOO = bar
endif

Оператор назначения из шелла '!=' (shell assignment operator) может использоваться для запуска скрипта и установки переменной на его выходе. Этот оператор сначала вычисляет свою правую часть, затем передает результат в шелл для выполнения. Если результата заканчивается в новой строке, то эта новая строка удаляется; все другие новые строки заменяются пробелами. Результирующая строка затем помещается в именованную рекурсивно развернутую переменную. Например:

hash != printf '\043'
file_list != find . -name '*.c'

Если результат выполнения может создать $, и вы не хотели бы интерпретировать это как make-переменную или ссылку на функцию, то должны заменить каждый символ $ на $$ как часть выполнения. Альтернативно вы можете задать просто развернутую переменную в результате выполнения программы с помощью вызова функции shell (см. описание функции shell [6]). Например:

hash := $(shell printf '\043')var := $(shell find . -name "*.c")

С функцией shell статус выполнения непосредственно вызванного shell-скрипта сохраняется в переменной .SHELLSTATUS.

Как получить значение переменной окружения. Если вы сделали экспорт переменной окружения в консоли и скрипте bash:

export demoPath=/usr/local/demo

... то можете просто извлечь значение этой переменной в makefile (утилита make импортирует все переменные окружения, которые вами были установлены):

DEMOPATH = ${demoPath}

Или так:

DEMOPATH = $(demoPath)

Переменная окружения не будет доступна, пока не сделан её экспорт. Как альтернатива переменную окружения можно передать через командную строку make:

$ make DEMOPATH="${demoPath}" ...

Если вы используете производные от C shell, замените команду export комбинацией setenv demoPath /usr/local/demo.

[Ссылки]

1. Makefile Setting Variables site:gnu.org.
2. Makefile Splitting Long Lines site:gnu.org.
3. Makefile Variables Used by Implicit Rules site:gnu.org.
4. Makefile Automatic Variables site:gnu.org.
5. Makefile The origin Function site:gnu.org.
6. Makefile The shell Function site:gnu.org.
7. How to get a shell environment variable in a makefile? site:stackoverflow.com.
8Как устроен Makefile и что это такое?

 

Добавить комментарий


Защитный код
Обновить

Top of Page