Comment imprimer une variable dans un makefile


247

Dans mon makefile, j'ai une variable 'NDK_PROJECT_PATH', ma question est de savoir comment l'imprimer lors de sa compilation?

J'ai lu Make echo file affichant la chaîne "$ PATH" et j'ai essayé:

@echo $(NDK_PROJECT_PATH)
@echo $(value NDK_PROJECT_PATH)

Les deux me donnent

"build-local.mk:102: *** missing separator.  Stop."

Quelqu'un sait pourquoi cela ne fonctionne pas pour moi?

Réponses:


223

Vous pouvez imprimer des variables pendant la lecture du makefile (en supposant que GNU make comme vous avez correctement tagué cette question) en utilisant cette méthode (avec une variable nommée "var"):

$(info $$var is [${var}])

Vous pouvez ajouter cette construction à n'importe quelle recette pour voir quelle marque passera au shell:

.PHONY: all
all: ; $(info $$var is [${var}])echo Hello world

Maintenant, ce qui se passe ici, c'est que make stocke l'intégralité de la recette ( $(info $$var is [${var}])echo Hello world) comme une seule variable développée récursivement. Lorsque make décide d'exécuter la recette (par exemple lorsque vous lui dites de construire all), il développe la variable, puis passe chaque ligne résultante séparément au shell.

Donc, dans les moindres détails:

  • Il se dilate $(info $$var is [${var}])echo Hello world
  • Pour ce faire, il se développe d'abord $(info $$var is [${var}])
    • $$ devient littéral $
    • ${var}devient :-)(disons)
    • L'effet secondaire est qu'il $var is [:-)]apparaît sur la sortie standard
    • L'expansion du $(info...)bien est vide
  • Faire est laissé avec echo Hello world
    • Faites d'abord des impressions echo Hello worldsur stdout pour vous faire savoir ce que cela va demander au shell
  • La coquille imprime Hello worldsur stdout.

2
devrait-il être (point) .PHONY au lieu de PHONY?
hammadian

172

Selon le manuel GNU Make et également indiqué par «bobbogo» dans la réponse ci-dessous, vous pouvez utiliser info / warning / error pour afficher du texte.

$(error   text…)
$(warning text…)
$(info    text…)

Pour imprimer des variables,

$(error   VAR is $(VAR))
$(warning VAR is $(VAR))
$(info    VAR is $(VAR))

'error' arrêterait l' exécution de make , après avoir montré la chaîne d'erreur


Wow, je ne savais pas ça. Bien mieux que l'écho, qui duplique la commande dans les journaux.
deepelement


44

Si vous voulez simplement une sortie, vous voulez l'utiliser $(info)seule. Vous pouvez le faire n'importe où dans un Makefile, et cela s'affichera lorsque cette ligne sera évaluée:

$(info VAR="$(VAR)")

Sortira VAR="<value of VAR>"chaque fois que les processus seront traités sur cette ligne. Ce comportement est très dépendant de la position, vous devez donc vous assurer que l' $(info)expansion se produit APRÈS tout ce qui pourrait être modifié $(VAR)s'est déjà produit!

Une option plus générique consiste à créer une règle spéciale pour imprimer la valeur d'une variable. De manière générale, les règles sont exécutées après que les variables sont affectées, cela vous montrera donc la valeur réellement utilisée. (Cependant, il est possible qu'une règle modifie une variable .) Un bon formatage aidera à clarifier à quoi une variable est définie, et la $(flavor)fonction vous dira de quel type de variable il s'agit. Donc dans cette règle:

print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
  • $*se développe jusqu'à la tige à laquelle le % modèle correspond dans la règle.
  • $($*)se développe à la valeur de la variable dont le nom est donné par $*.
  • Le [et ]délimiter clairement l'expansion variable. Vous pouvez également utiliser "et "ou similaire.
  • $(flavor $*)vous indique de quel type de variable il s'agit. REMARQUE: $(flavor) prend un nom de variable et non son expansion. Donc, si vous dites make print-LDFLAGS, vous obtenez $(flavor LDFLAGS), c'est ce que vous voulez.
  • $(info text)fournit une sortie. Faites des impressions textsur sa sortie standard comme effet secondaire de l'expansion. L'expansion de $(info)bien est vide. Vous pouvez y penser comme @echo, mais surtout, il n'utilise pas le shell, vous n'avez donc pas à vous soucier des règles de citation du shell.
  • @trueest là juste pour fournir une commande pour la règle. Sans cela, make sortira également print-blah is up to date. Je pense @truequ'il est plus clair que c'est censé être un non-op.

En l'exécutant, vous obtenez

$ make print-LDFLAGS
LDFLAGS is a recursive variable set to [-L/Users/...]

Cela ne fournit pas de réponse à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous son article - vous pouvez toujours commenter vos propres articles, et une fois que vous aurez une réputation suffisante, vous pourrez commenter n'importe quel article . - De l'avis
Dragon Energy

Merci pour l'examen. Je comprends que je peux commenter une fois que j'ai suffisamment de réputation, mais que dois-je faire en attendant? Je crois que ma réponse apporte une valeur supplémentaire, alors ne devrais-je pas l'ajouter?
Jim Nasby

1
Je suggérerais de réécrire ceci pour être une réponse autonome en concurrence avec celle sur laquelle vous commentez, plutôt que de l'avoir formulée comme un commentaire inapproprié à cette réponse.
Charles Duffy

@ JimNasby Oui, ce que Charles a suggéré. En fait, il pourrait être utile de commencer par supprimer simplement cette partie "désolé, pas assez de représentant pour commenter", car c'est une sorte de drapeau rouge. Si vous la transformez en une réponse complète (vous pouvez la modifier à votre guise), cela devrait fonctionner très bien. À votre santé.
Dragon Energy

Excellent - c'est une bien meilleure réponse maintenant. :)
Charles Duffy

6

@echo $ (NDK_PROJECT_PATH) est le bon moyen de le faire. Je ne pense pas que l'erreur vient de là. Généralement, cette erreur apparaît lorsque vous avez mal saisi l'intention: je pense que vous avez des espaces où vous devriez avoir une tabulation.


2
J'ai essayé '@echo $ (NDK_PROJECT_PATH)', j'obtiens toujours une erreur "build-local.mk:102: *** séparateur manquant. Arrêtez.". J'ai juste 1 espace après 'echo', il n'y a ni espace ni tabulation avant '@echo'.
michael

Êtes-vous sûr que l'erreur provient de cette ligne? Cette ligne est-elle dans une règle? Elle doit probablement être en retrait ...

6

Toutes les versions de makenécessitent que les lignes de commande soient mises en retrait avec un TAB (pas d'espace) comme premier caractère de la ligne. Si vous nous montriez la règle entière au lieu des deux lignes en question, nous pourrions donner une réponse plus claire, mais cela devrait être quelque chose comme:

myTarget: myDependencies
        @echo hi

où le premier caractère de la deuxième ligne doit être TAB.


4

Run make -n; il vous montre la valeur de la variable ..

Makefile ...

all:
        @echo $(NDK_PROJECT_PATH)

Commander:

export NDK_PROJECT_PATH=/opt/ndk/project
make -n 

Production:

echo /opt/ndk/project

C'est assez utile et j'utilise --just-printle même dans Au lieu de l'exécution
Fredrick Gauss

3

Cela makefilegénérera le message d'erreur «séparateur manquant»:

all
    @echo NDK_PROJECT_PATH=$(NDK_PROJECT_PATH)

done:
        @echo "All done"

Il y a un onglet avant le @echo "All done"(bien que la done:règle et l'action soient largement superflues), mais pas avant le @echo PATH=$(PATH).

Le problème est que la ligne commençant alldoit avoir deux points :ou un égal =pour indiquer qu'il s'agit d'une ligne cible ou d'une macro, et qu'elle n'a ni l'une ni l'autre, de sorte que le séparateur est manquant.

L'action qui fait écho à la valeur d'une variable doit être associée à une cible, éventuellement une cible factice ou PHONEY. Et cette ligne cible doit avoir un deux-points dessus. Si vous ajoutez un :après alldans l'exemple makefileet remplacez les blancs en tête de la ligne suivante par un onglet, cela fonctionnera correctement.

Vous avez probablement un problème analogue près de la ligne 102 dans l'original makefile. Si vous montriez 5 lignes non vides et sans commentaire avant les opérations d'écho qui échouent, il serait probablement possible de terminer le diagnostic. Cependant, puisque la question a été posée en mai 2013, il est peu probable que le cassé makefilesoit toujours disponible maintenant (août 2014), donc cette réponse ne peut pas être validée formellement. Il ne peut être utilisé que pour illustrer une manière plausible dont le problème s'est produit.


3

Pas besoin de modifier le Makefile.

$ cat printvars.mak
print-%:
        @echo '$*=$($*)'

$ cd /to/Makefile/dir
$ make -f ~/printvars.mak -f Makefile print-VARIABLE

2

Le problème est que l'écho ne fonctionne que sous un bloc d'exécution. c'est-à-dire quoi que ce soit après "xx:"

Donc, tout ce qui se trouve au-dessus du premier bloc d'exécution n'est qu'une initialisation, donc aucune commande d'exécution ne peut être utilisée.

Alors créez un bloc d'exécution


1

Cela peut être fait de manière générique et peut être très utile lors du débogage d'un makefile complexe. En suivant la même technique que celle décrite dans une autre réponse , vous pouvez insérer ce qui suit dans n'importe quel makefile:

# if the first command line argument is "print"
ifeq ($(firstword $(MAKECMDGOALS)),print)

  # take the rest of the arguments as variable names
  VAR_NAMES := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

  # turn them into do-nothing targets
  $(eval $(VAR_NAMES):;@:))

  # then print them
  .PHONY: print
  print:
          @$(foreach var,$(VAR_NAMES),\
            echo '$(var) = $($(var))';)
endif

Ensuite, vous pouvez simplement faire "make print" pour vider la valeur de n'importe quelle variable:

$ make print CXXFLAGS
CXXFLAGS = -g -Wall


0

Si vous ne souhaitez pas modifier le Makefile lui-même, vous pouvez utiliser --evalpour ajouter une nouvelle cible, puis exécuter la nouvelle cible, par exemple

make --eval='print-tests: @echo TESTS $(TESTS) ' print-tests

Vous pouvez insérer le caractère TAB requis dans la ligne de commande à l'aide de CTRL-V, TAB

exemple Makefile d'en haut:

all: do-something

TESTS=
TESTS+='a'
TESTS+='b'
TESTS+='c'

do-something:
        @echo "doing something"
        @echo "running tests $(TESTS)"
        @exit 1

J'essaie d'exécuter votre script mais je reçois une erreurmake: *** missing separator. Stop.
Rinaldi Segecin

Ah, désolé, réparé. Deux points manquants à la fin de la cible "tests d'impression".
Wade

En fait, vous n'avez pas besoin de nouvelles lignes et d'onglets, un point-virgule suffira:make --eval='print-tests: ; @echo TESTS $(TESTS)' print-tests
bobbogo

0

si vous utilisez Android make (mka) @echo $(NDK_PROJECT_PATH)ne fonctionnera pas et vous donne une erreur, *** missing separator. Stop." utilisez cette réponse si vous essayez d'imprimer des variables dans Android make

NDK_PROJECT_PATH := some_value
$(warning $(NDK_PROJECT_PATH))

qui a fonctionné pour moi


0

J'ai généralement un écho avec une erreur si je voulais voir la valeur de la variable (uniquement si vous vouliez voir la valeur. Cela arrêtera l'exécution).

@echo $ (erreur NDK_PROJECT_PATH = $ (NDK_PROJECT_PATH))

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.