Как установить переменную среды дочернего процесса в Makefile

135

Я хотел бы изменить этот Makefile:

SHELL := /bin/bash
PATH  := node_modules/.bin:$(PATH)

boot:
    @supervisor         \
      --harmony         \
      --watch etc,lib       \
      --extensions js,json      \
      --no-restart-on error     \
        lib

test:
    NODE_ENV=test mocha         \
      --harmony             \
      --reporter spec       \
        test

clean:
    @rm -rf node_modules

.PHONY: test clean

чтобы:

SHELL := /bin/bash
PATH  := node_modules/.bin:$(PATH)

boot:
    @supervisor         \
      --harmony         \
      --watch etc,lib       \
      --extensions js,json      \
      --no-restart-on error     \
        lib

test: NODE_ENV=test
test:
    mocha                   \
      --harmony             \
      --reporter spec       \
        test

clean:
    @rm -rf node_modules

.PHONY: test clean

К сожалению, второй не работает (процесс узла по-прежнему работает со значением по умолчанию NODE_ENV.

Что я пропустил?

bodokaiser
источник
Ваш Unfortunatelyкомментарий связан с недоразумением между переменной среды и Makefileпеременной. Лучший способ доказать, что переменная среды установлена, - это запросить эту переменную среды внутри другой программы, которая будет makeвызывать. Только действие echo $(BLAH)- это просто оценка механизма ключа / значения Makefile внутри Makefile. В python вы можете print(os.getenv("MURDOC")) действительно запросить переменную среды.
truthadjustr

Ответы:

154

По умолчанию переменные make не экспортируются в среду процессов make invokes ... Однако вы можете использовать make, exportчтобы заставить их сделать это. Изменить:

test: NODE_ENV = test

к этому:

test: export NODE_ENV = test

(при условии, что у вас достаточно современная версия GNU make> = 3.77).

Злой ученый
источник
3
У меня GNU make 3.81 all: <\n\t>export PROJ_ROOT=$(CURDIR)<\n\t>echo $(PROJ_ROOT)<\n>выводит правильное расширение для первой строки, но только echoдля второй. PROJ_ROOTне устанавливается после запуска make. Пробелы вокруг =дают "неправильное имя переменной" для экспорта. Наличие первой строки в качестве предварительного условия, как в вашем примере, дает «команды начинаются до первой цели»
Готье
8
@Gauthier да, конечно. Я не об этом писал. Вы добавили <\ n \ t> после all:, чего нет в моем примере. Мой пример предназначен для использования в том виде, в каком он написан: он определяет переменную, специфичную для цели, а НЕ добавляет команду в рецепт. Также вы не можете использовать рецепт и переменную, зависящую от цели, на цели одновременно: вы должны написать цель дважды. См. Второй пример в вопросе и задайте новый вопрос, если это не помогает объяснить: в комментариях недостаточно места или форматирования.
MadScientist
1
А как насчет нескольких переменных?
Холмс
1
Это нормально, но вы добавили его в заголовок цели. Просто перечислить их в контексте цели не получится? Потому что у меня это не работает,
Холмс
2
Переменные для конкретных целей были добавлены в GNU make 3.77. Их можно экспортировать, начиная с версии GNU make 3.81. См. Git.savannah.gnu.org/cgit/make.git/tree/NEWS
MadScientist
80

Как указал MadScientist , вы можете экспортировать отдельные переменные с помощью:

export MY_VAR = foo  # Available for all targets

Или экспортировать переменные для конкретной цели ( целевой конкретные переменные ):

my-target: export MY_VAR_1 = foo
my-target: export MY_VAR_2 = bar
my-target: export MY_VAR_3 = baz

my-target: dependency_1 dependency_2
  echo do something

Вы также можете указать .EXPORT_ALL_VARIABLESцель - как вы уже догадались! - ЭКСПОРТИРОВАТЬ ВСЕ ВСЕ !!!:

.EXPORT_ALL_VARIABLES:

MY_VAR_1 = foo
MY_VAR_2 = bar
MY_VAR_3 = baz

test:
  @echo $$MY_VAR_1 $$MY_VAR_2 $$MY_VAR_3

см. .EXPORT_ALL_VARIABLES

Шаммель Ли
источник
2
Как ни странно, я проверил это раньше, и это показало, что он работает ... (не знаю, почему сейчас ..) Я могу вернуться и удалить комментарий, я думаю ...
AnthonyC
3
@AnthonyC Это работает, потому что есть два MY_VARs: один - это переменная makefile, доступ к которой осуществляется как, ${MY_VAR}а другой - переменная, экспортируемая из bash, доступная как$$MY_VAR
Сергей
Полезно. Но не могу найти способ экспортировать только набор переменных.
Эрик Чен
14

Мне нужны были только переменные среды локально, чтобы вызвать мою тестовую команду, вот пример, устанавливающий несколько переменных среды в оболочке bash и избегающий входа в систему make.

SHELL := /bin/bash

.PHONY: test tests
test tests:
    PATH=./node_modules/.bin/:$$PATH \
    JSCOVERAGE=1 \
    nodeunit tests/
ThorSummoner
источник
6
Измените свой ответ, включив в него некоторые пояснения. Ответы только на коде мало что могут сделать для обучения будущих читателей SO. Ваш ответ находится в очереди на модерацию как некачественный.
mickmackusa
ThorSummoner, это решение не так гибко, как вышеприведенный подход. Например, кто-то может пожелать иметь одно правило для вызова команды, а затем несколько других правил, которые изменяют это поведение, устанавливая переменные среды. Рассмотрим: test: cmd perf: export PERF = "yes" perf: test Если cmd сложен (а это обычно бывает), то этот подход намного проще поддерживать. Ваш подход к установке переменной среды в правиле cmd усложняет эту задачу.
Keith
1

Я бы переписал исходный целевой тест, позаботившись о том, чтобы нужная переменная была определена В ЖЕ ПОДПРОЦЕССЕ как запускаемое приложение:

test:
    ( NODE_ENV=test mocha --harmony --reporter spec test )
Guiyo M
источник