web-dev-qa-db-fra.com

Initialisation et exportation de variables Makefile

somevar := Apple
export somevar
update := $(Shell echo "v=$$somevar")

all:
    @echo $(update)

J'espérais Apple en sortie de commande, mais il est vide, ce qui me fait penser à exporter et := expansion variable se déroulant sur différentes phases. comment surmonter cela?

31
Pablo

Le problème est que export exporte la variable dans les sous-coquilles utilisées par les commandes; il n'est pas disponible pour une extension dans d'autres affectations. N'essayez donc pas de l'obtenir de l'environnement en dehors d'une règle.

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"'
33
Beta

@ Beta's answer contient le pointeur crucial: avec GNU make, les variables marquées avec export ne sont disponibles que pour [les shells lancés pour] commandes de recette (commandes qui font partie des règles), malheureusement pas aux invocations de $(Shell ...) (ils ne voient que l'environnement que make lui-même a vu lors de son lancement).

Il existe une solution de contournement , cependant: explicitement passez la variable exportée en tant que variable d'environnement à la fonction Shell:

update := $(Shell somevar='$(somevar)' Perl -e 'print "$$ENV{somevar}"')

En ajoutant la commande Shell à <var>=<val>, Cette définition est ajoutée en tant que variable d'environnement à l'environnement que la commande voit - il s'agit d'une fonction générique Shell.

Avertissement : @Kaz souligne dans un commentaire que cette méthode se comporte mal si $(somevar) contient certains caractères., Car l'expansion de la variable est = verbatim (aucun échappement), qui peut casser la commande Shell résultante, et suggère la variante suivante pour fonctionner également avec les instances ' incorporées (divise la valeur d'entrée en sous-chaînes entre guillemets simples avec ' épissé):

update := $(Shell somevar='$(subst ','\'',$(somevar))' Perl -e 'print "$$ENV{somevar}"')

Cela devrait fonctionner avec toutes les valeurs sauf multiligne celles (qui sont rares; il n'y a pas de solution pour les valeurs multilignes que je connaisse).

Sur une note latérale, les caractères littéraux $. dans les valeurs doivent être représentées par $$, sinon make les interprétera comme des références à ses propres variables.

Notez que je n'ai délibérément PAS choisi l'instruction OP - original, update := $(Shell echo "v=$$somevar"), pour la démonstration, car elle contient un piège qui embrouille le problème: en raison de la façon dont le shell évalue un ligne de commande, somevar=Apple echo v=$somevar n'évalue PAS en v=Apple, car la référence $somevar est développée avantsomevar=Apple prend effet. Pour obtenir l'effet souhaité dans ce cas, vous devez utiliser les instructions 2: update := $(Shell export somevar="$(somevar)"; echo "v=$$somevar")


Quant au débat bug-contre-fonctionnalité:

Bien que l'on puisse affirmer que la fonction Shell devrait voir le même environnement que les commandes de recette, la documentation ne fait aucune promesse - voir http: // www. gnu.org/software/make/manual/make.html#Shell-Function . Inversement, http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion mentionne uniquement la mise à disposition des variables exportées aux commandes - recette.

15
mklement0

Exécuter le makefile

foo:=Apple
export foo
all:
        @echo ">"$(Shell echo "$$foo")
        @echo ">""$$foo"

donne pour moi (avec foo indéfini dans l'environnement)

$ make
>
>Apple

$ make foo=bar
>
>Apple

$ export foo=bar; make
>bar
>Apple

$ export foo=bar; make foo=bar
>bar
>bar

Essayez d'utiliser le formulaire cité (update := "v=$$somevar") et laissez le Shell gérer l'expansion lorsqu'une commande est exécutée (vous aurez toujours besoin de l'exportation)

10
Scott Wales

Bien que export ne fonctionne pas bien avec $(Shell ...), il existe une solution de contournement simple. Nous pouvons transmettre les données au script Shell via la ligne de commande.

Maintenant, bien sûr, le passage de l'environnement est robuste contre les problèmes d'échappement et de citation. Cependant, le langage Shell a une méthode de guillemet simple '...' Qui gère tout. Le seul problème est qu'il n'y a aucun moyen d'obtenir un seul devis là-dedans; mais bien sûr, cela est résolu en mettant fin à la citation, en évitant la barre oblique inverse et en commençant une nouvelle citation: en d'autres termes:

ab'cd -> 'ab'\''cd'

Dans le script Shell exécuté par $(Shell ...) nous générons simplement une affectation de variable de la forme var='$(...)', où $(...) est une expression make qui interpole le matériel échappé de manière appropriée. Ainsi, 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

Exemple d'exécution:

$ make
cat file.txt
Apple with 'quoted' "stuff" and dollar $signs

Si nous voulons communiquer une variable d'environnement à une commande, nous pouvons le faire en utilisant la syntaxe Shell VAR0=val0 VAR1=val1 ... VARn=valn command arg .... Cela peut être illustré par quelques modifications mineures au Makefile ci-dessus:

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

Courir:

$ make
grep somevar file.txt
somevar=Apple with 'quoted' "stuff" and dollar $signs

file.txt Contient un vidage des variables d'environnement, où nous pouvons voir somevar. Si export in GNU Make avait fait la bonne chose, nous aurions pu simplement faire:

export somevar
update := $(Shell env > file.txt)

mais le résultat final est le même.

Puisque le résultat final que vous voulez est de echo $(update), vous feriez Shell_escape De toute façon, même si GNU Rendre les vars exportés passés à $(Shell ...) C'est-à-dire, regardez un autre 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)

Production:

Apple with 'quoted' "stuff" and dollar $signs
Apple with quoted stuff and dollar
1
Kaz