Инициализация и экспорт переменных файла makefile
somevar := apple
export somevar
update := $(shell echo "v=$$somevar")
all:
@echo $(update)
Я надеялся на яблоко как на выход команды, но он пуст, что заставляет меня думать, что экспорт и расширение :=
происходит на разных этапах. как преодолеть это?
Ответы
Ответ 1
Проблема заключается в том, что export
экспортирует переменную в подоболочки, используемые командами; он недоступен для расширения в других назначениях. Поэтому не пытайтесь получить его из среды вне правила.
somevar := apple
export somevar
update1 := $(shell perl -e 'print method 1 "$$ENV{somevar}\n"')
# Make runs the shell command, the shell does not know somevar, so update1 is "method 1 ".
update2 := perl -e 'print method 2 "$$ENV{somevar}\n"'
# Now update2 is perl -e 'print method 2 "$$ENV{somevar}\n"'
# Lest we forget:
update3 := method 3 $(somevar)
all:
echo $(update1)
$(update2)
$echo $(update3)
perl -e 'print method 4 "$$ENV{somevar}\n"'
Ответ 2
@Beta answer содержит ключевой указатель: с GNU make
, переменные, помеченные знаком export
, доступны только [для запущенных оболочек] команды рецептов (команды, которые являются частью правил), к сожалению не к вызовам $(shell ...)
(они видят только среду, которую make
сама увидела, когда она была запущена).
Существует обходной путь: явно передать экспортированную переменную как переменную среды в функцию shell
:
update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"')
Добавив команду оболочки с помощью <var>=<val>
, это определение добавляется как переменная среды в среду, которую видит команда - это общая оболочечная функция.
Caveat: @Kaz указывает в комментарии, что этот метод неверно работает, если $(somevar)
содержит определенные символы. Поскольку расширение переменной является дословным (без экранирования), которое может разорвать полученную команду оболочки, и предлагает следующий вариант , который также работает со встроенными '
экземплярами (разбивает входное значение на одноколейные подстроки с кавычками '
):
update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')
Это должно работать со всеми значениями, за исключением многострочных (которые встречаются редко, нет обходного пути для многострочных значений, о которых я знаю).
На стороне примечание, буквальный $
символ. в значениях должны быть представлены как $$
, иначе make
интерпретирует их как ссылки на свои собственные переменные.
Обратите внимание, что я специально не выбрал оригинальную инструкцию OP, update := $(shell echo "v=$$somevar")
, для демонстрации, потому что она содержит ошибку, которая путает проблему: из-за того, как оболочка оценивает командную строку, somevar=apple echo v=$somevar
НЕ оценивает значение v=apple
, так как ссылка $somevar
расширяется до того, как эффект somevar=apple
вступает в силу. Для достижения желаемого эффекта в этом случае вам нужно будет использовать 2 оператора: update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")
Что касается обсуждения ошибок с ошибкой:
Хотя можно утверждать, что функция shell
должна видеть ту же среду, что и команды рецептов, документация не дает такого обещания - см. http://www.gnu.org/software/make/manual/make.html#Shell-Function. И наоборот, http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion упоминает только, что экспортированные переменные доступны для команд рецептов.
Ответ 3
Запуск make файла
foo:=apple
export foo
all:
@echo ">"$(shell echo "$$foo")
@echo ">""$$foo"
дает мне (с foo undefined в среде)
$ make
>
>apple
$ make foo=bar
>
>apple
$ export foo=bar; make
>bar
>apple
$ export foo=bar; make foo=bar
>bar
>bar
Попробуйте использовать цитированную форму (update := "v=$$somevar"
), и пусть оболочка обрабатывает расширение при выполнении команды (вам все равно понадобится экспорт)
Ответ 4
Хотя export
не играет хорошо с $(shell ...)
, есть простой способ обхода. Мы можем передать данные в оболочку script через командную строку.
Теперь, конечно, прохождение среды устойчиво к проблемам ускользания и цитирования. Тем не менее, язык оболочки имеет метод цитирования одной кавычки '...'
, который обрабатывает все. Единственная проблема заключается в том, что нет никакой возможности получить там одну цитату; но, конечно, это решается путем прекращения цитаты, обратной косой черты - сбрасывания нужной одинарной кавычки и начала новой цитаты: Другими словами:
ab'cd -> 'ab'\''cd'
В оболочке script, выполняемой $(shell ...)
, мы просто генерируем присваивание переменной формы var='$(...)'
, где $(...)
- это некоторое выражение, которое интерполирует подходящий экранированный материал. Таким образом, Makefile
:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v > file.txt)
.phony: all
all:
cat file.txt
Пример прогона:
$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs
Если мы хотим передать переменную окружения команде, мы можем это сделать, используя синтаксис оболочки VAR0=val0 VAR1=val1 ... VARn=valn command arg ...
. Это можно проиллюстрировать некоторыми незначительными изменениями выше Makefile
:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell somevar='$(call shell_escape,$(somevar))' env > file.txt)
.phony: all
all:
grep somevar file.txt
Run:
$ make
grep somevar file.txt
somevar=apple with 'quoted' "stuff" and dollar $signs
file.txt
содержит дамп переменных среды, где мы можем видеть somevar
. Если export
в GNU Make сделал правильные вещи, мы могли бы просто сделать:
export somevar
update := $(shell env > file.txt)
но конечный результат тот же.
Поскольку конечный результат, который вы хотите, это echo $(update)
, вы все равно должны были бы shell_escape
, даже если GNU Make передал экспортированные vars на $(shell ...)
. Другими словами, посмотрите еще один Makefile
:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v)
.phony: all
all:
@echo '$(call shell_escape,$(update))'
@echo $(update)
Вывод:
apple with 'quoted' "stuff" and dollar $signs
apple with quoted stuff and dollar