Jak ustawić zmienną środowiskową procesu potomnego w Makefile


143

Chciałbym zmienić ten plik 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

do:

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

Niestety drugi nie działa (proces węzła nadal działa z domyślnym NODE_ENV.

Co mnie ominęło?


Twój Unfortunatelykomentarz wynika z nieporozumienia między zmienną środowiskową a Makefilezmienną. Najlepszym sposobem udowodnienia, że ​​zmienna środowiskowa została ustawiona, jest makewysłanie zapytania do tej zmiennej w innym programie, który zostanie wywołany. Jedynym działaniem echo $(BLAH)jest po prostu ocena mechanizmu klucza / wartości Makefile wewnątrz Makefile. W Pythonie możesz print(os.getenv("MURDOC")) naprawdę zapytać o zmienną środowiskową.
eigenfield

Odpowiedzi:


159

Zmienne make nie są domyślnie eksportowane do środowiska procesów make invokes ... Możesz jednak użyć marek, exportaby je do tego zmusić. Zmiana:

test: NODE_ENV = test

do tego:

test: export NODE_ENV = test

(zakładając, że masz wystarczająco nowoczesną wersję GNU make> = 3.77).


3
Mam GNU make 3.81 i all: <\n\t>export PROJ_ROOT=$(CURDIR)<\n\t>echo $(PROJ_ROOT)<\n>wyprowadza poprawne rozszerzenie dla pierwszego wiersza, ale tylko echodla drugiego. PROJ_ROOTnie jest ustawiana po uruchomieniu make. Spacje wokół =dają „złą nazwę zmiennej” do eksportu. Pierwszy wiersz jako warunek wstępny, jak w przykładzie, daje „polecenia rozpoczynające się przed pierwszym celem”
Gauthier

8
@Gauthier tak, oczywiście. Nie o tym napisałem. Dodałeś <\ n \ t> po znaku all:, którego nie ma w moim przykładzie. Mój przykład ma być używany tak, jak napisano: definiuje zmienną specyficzną dla celu, NIE dodaje polecenia do receptury. Nie możesz również użyć receptury i zmiennej specyficznej dla celu w tym samym czasie: musisz napisać cel dwukrotnie. Zobacz drugi przykład w pytaniu i zadaj nowe pytanie, jeśli to nie pomaga w wyjaśnieniu: nie ma wystarczającej ilości miejsca lub formatowania w komentarzach.
MadScientist

1
A co z wieloma zmiennymi?
holms

1
W porządku, ale dodałeś to do głowy celu. Samo wymienienie ich w kontekście celu nie zadziała? Ponieważ to nie działa dla mnie
holms

2
Zmienne specyficzne dla celu zostały dodane w GNU make 3.77. Można je eksportować od wersji GNU 3.81. Zobacz git.savannah.gnu.org/cgit/make.git/tree/NEWS
MadScientist

93

Jak wskazał MadScientist , możesz wyeksportować poszczególne zmienne za pomocą:

export MY_VAR = foo  # Available for all targets

Lub zmienne eksportowych dla określonej docelowej ( target-specyficzne zmienne ):

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

Możesz także określić .EXPORT_ALL_VARIABLEScel - zgadłeś! - EKSPORTUJ WSZYSTKIE RZECZY !!!:

.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

zobacz .EXPORT_ALL_VARIABLES


2
Co dziwne, przetestowałem go wcześniej i pokazałem, że działa .. (nie wiem dlaczego teraz ..) Mogę cofnąć się i usunąć komentarz chyba ..
AnthonyC

3
@AnthonyC Działa, ponieważ istnieją dwa MY_VARs: jeden to zmienna makefile, do której dostęp jest dostępna jako, ${MY_VAR}a druga to zmienna eksportowana bash, dostępna jako$$MY_VAR
Sergei

Przydatny. Ale nie mogę znaleźć sposobu na wyeksportowanie tylko zestawu zmiennych.
Eric Chen

15

Potrzebowałem zmiennych środowiskowych tylko lokalnie, aby wywołać moje polecenie testowe, oto przykład ustawiania wielu zmiennych środowiskowych w powłoce bash i unikania znaku dolara make.

SHELL := /bin/bash

.PHONY: test tests
test tests:
    PATH=./node_modules/.bin/:$$PATH \
    JSCOVERAGE=1 \
    nodeunit tests/

6
Zmień swoją odpowiedź, dołączając wyjaśnienie. Odpowiedzi zawierające tylko kod w niewielkim stopniu kształcą przyszłych czytelników SO. Twoja odpowiedź znajduje się w kolejce moderacji za niską jakość.
mickmackusa

ThorSummoner, to rozwiązanie nie jest tak elastyczne, jak powyższe podejście. Na przykład, ktoś może chcieć mieć jedną regułę wywołującą polecenie, a następnie kilka innych reguł, które modyfikują to zachowanie poprzez ustawienie zmiennych środowiskowych. Zastanów się: test: cmd perf: export PERF = "yes" perf: test Jeśli 'cmd' jest skomplikowane (i zwykle jest), to takie podejście jest dużo łatwiejsze do utrzymania. Twoje podejście do ustawiania zmiennej środowiskowej w regule cmd utrudnia to.
Keith Hanlan

2

Chciałbym ponownie napisać oryginalny test docelowy, uważając, aby potrzebna zmienna została zdefiniowana W TYM SAMYM PODPROCESIE co aplikacja do uruchomienia:

test:
    ( NODE_ENV=test mocha --harmony --reporter spec test )
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.