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. |