Comment définir la variable d'environnement du processus enfant dans Makefile

135

Je voudrais changer ce 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

Malheureusement, le second ne fonctionne pas (le processus de nœud s'exécute toujours avec la valeur par défaut NODE_ENV .

Qu'est-ce que j'ai raté?

bodokaiser
la source
Votre Unfortunatelycommentaire découle d'un malentendu entre une variable d'environnement et une Makefilevariable. La meilleure façon de prouver qu'une variable d'environnement a été définie est d'interroger cette variable d'environnement dans un autre programme qui l' makeappellera. Il echo $(BLAH)suffit d'évaluer le mécanisme clé / valeur de Makefile dans le Makefile. En python, vous pouvez print(os.getenv("MURDOC")) vraiment interroger la variable d'environnement.
truthadjustr il y a

Réponses:

154

Les variables Make ne sont pas exportées dans l'environnement des processus make invokes ... par défaut. Cependant, vous pouvez utiliser des make exportpour les forcer à le faire. Changement:

test: NODE_ENV = test

pour ça:

test: export NODE_ENV = test

(en supposant que vous ayez une version suffisamment moderne de GNU make> = 3.77).

Scientifique fou
la source
3
J'ai GNU make 3.81, et all: <\n\t>export PROJ_ROOT=$(CURDIR)<\n\t>echo $(PROJ_ROOT)<\n>affiche l'extension correcte pour la première ligne, mais uniquement echopour la seconde. PROJ_ROOTn'est pas défini après l'exécution de make. Les espaces autour =donnent un "mauvais nom de variable" pour l'exportation. Avoir la première ligne comme prérequis comme dans votre exemple donne "les commandes commencent avant la première cible"
Gauthier
8
@Gauthier oui bien sûr. Ce n'est pas ce que j'ai écrit. Vous avez ajouté un <\ n \ t> après le all:, ce qui n'est pas dans mon exemple. Mon exemple est destiné à être utilisé tel qu'il est écrit: il définit une variable spécifique à la cible, PAS l' ajout d'une commande à la recette. De plus, vous ne pouvez pas utiliser une recette et une variable spécifique à une cible en même temps: vous devez écrire la cible deux fois. Consultez le deuxième exemple de la question et posez une nouvelle question si cela ne permet pas de l'expliquer: il n'y a pas assez d'espace ou de mise en forme dans les commentaires.
MadScientist
1
Qu'en est-il de plusieurs variables?
holms
1
C'est bien, mais vous l'avez ajouté à la tête de la cible. Les énumérer dans le contexte de la cible ne fonctionnera pas? Parce que ça ne marche pas pour moi
holms
2
Des variables spécifiques à la cible ont été ajoutées dans GNU make 3.77. Ils sont exportables à partir de la marque GNU 3.81. Voir git.savannah.gnu.org/cgit/make.git/tree/NEWS
MadScientist
80

Comme l'a souligné MadScientist , vous pouvez exporter des variables individuelles avec:

export MY_VAR = foo  # Available for all targets

Ou exporter des variables pour une cible spécifique ( variables spécifiques à la cible ):

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

Vous pouvez également spécifier la .EXPORT_ALL_VARIABLEScible à - vous l'avez deviné! - EXPORTER TOUTES LES CHOSES !!!:

.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

voir .EXPORT_ALL_VARIABLES

Shammel Lee
la source
2
Curieusement, je l'ai testé plus tôt, ce qui a montré que cela fonctionnait .. (je ne sais pas pourquoi maintenant ..) Je peux revenir en arrière et supprimer le commentaire, je suppose ..
AnthonyC
3
@AnthonyC Cela fonctionne car il y a deux MY_VARs: l'un est la variable makefile accessible en tant que ${MY_VAR}et l'autre est la variable exportée bash, accessible en tant que$$MY_VAR
Sergei
Utile. Mais impossible de trouver un moyen d'exporter uniquement un ensemble de variables.
Eric Chen
14

Je n'avais besoin que des variables d'environnement localement pour appeler ma commande de test, voici un exemple de définition de plusieurs variables d'environnement dans un shell bash et d'échappatoire au signe dollar make.

SHELL := /bin/bash

.PHONY: test tests
test tests:
    PATH=./node_modules/.bin/:$$PATH \
    JSCOVERAGE=1 \
    nodeunit tests/
ThorSummoner
la source
6
Veuillez modifier votre réponse pour inclure des explications. Les réponses basées uniquement sur le code font très peu pour éduquer les futurs lecteurs SO. Votre réponse est dans la file d'attente de modération pour être de mauvaise qualité.
mickmackusa
ThorSummoner, cette solution n'est pas aussi flexible que l'approche ci-dessus. Par exemple, on peut souhaiter avoir une seule règle pour appeler une commande, puis plusieurs autres règles qui modifient ce comportement en définissant des variables d'environnement. Considérez: test: cmd perf: export PERF = "yes" perf: test Si 'cmd' est compliqué (et c'est généralement le cas), alors cette approche est beaucoup plus facile à maintenir. Votre approche de la définition de la variable d'environnement dans la règle cmd rend cela plus difficile.
Keith Hanlan le
1

Je réécrirais le test cible d'origine, en veillant à ce que la variable nécessaire soit définie DANS LE MÊME SOUS-PROCESSUS que l'application à lancer:

test:
    ( NODE_ENV=test mocha --harmony --reporter spec test )
Guiyo M
la source