So drucken Sie eine Variable im Makefile aus

246

In meinem Makefile habe ich eine Variable 'NDK_PROJECT_PATH'. Meine Frage ist, wie ich sie beim Kompilieren ausdrucken kann.

Ich habe Make file echo mit der Zeichenfolge "$ PATH" gelesen und versucht:

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

Beides gibt mir

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

Weiß jemand, warum es bei mir nicht funktioniert?

Michael
quelle

Antworten:

221

Mit dieser Methode (mit einer Variablen namens "var") können Sie Variablen ausdrucken, während das Makefile gelesen wird (vorausgesetzt, GNU make, da Sie diese Frage entsprechend markiert haben):

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

Sie können dieses Konstrukt zu jedem Rezept hinzufügen, um zu sehen, welche Marke an die Shell übergeben wird:

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

Was hier passiert, ist, dass make das gesamte Rezept ( $(info $$var is [${var}])echo Hello world) als einzelne rekursiv erweiterte Variable speichert . Wenn make beschließt, das Rezept auszuführen (z. B. wenn Sie es zum Erstellen anweisen all), erweitert es die Variable und übergibt dann jede resultierende Zeile separat an die Shell.

Also, in schmerzhaften Details:

  • Es dehnt sich aus $(info $$var is [${var}])echo Hello world
  • Dazu wird es zuerst erweitert $(info $$var is [${var}])
    • $$ wird wörtlich $
    • ${var}wird :-)(sagen)
    • Der Nebeneffekt ist, dass $var is [:-)]auf Standard out erscheint
    • Die Erweiterung des $(info...)Obwohl ist leer
  • Make bleibt mit echo Hello world
    • Machen Sie zuerst Drucke echo Hello worldauf stdout, um Sie wissen zu lassen, wozu die Shell aufgefordert wird
  • Die Shell wird Hello worldauf Standard gedruckt .
Bobbogo
quelle
2
sollte es (Punkt) .PHONY statt PHONY sein?
Hammadian
171

Gemäß dem GNU Make-Handbuch und in der folgenden Antwort auch mit 'bobbogo' angegeben, können Sie mit info / warning / error Text anzeigen.

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

Um Variablen zu drucken,

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

'error' würde die Ausführung von make stoppen , nachdem die Fehlerzeichenfolge angezeigt wurde

Tsenapathie
quelle
Wow, wusste das nicht. Viel besser als das Echo, das den Befehl in den Protokollen dupliziert.
Deepelement
148

aus einem "Mr. Make Post" https://www.cmcrossroads.com/article/printing-value-makefile-variable

Fügen Sie Ihrem Makefile die folgende Regel hinzu:

print-%  : ; @echo $* = $($*)

Wenn Sie dann den Wert einer Makefile-Variablen herausfinden möchten, gehen Sie einfach wie folgt vor:

make print-VARIABLE

und es wird zurückkehren:

VARIABLE = the_value_of_the_variable
Anthony Earl Wong
quelle
Sie haben es besser erklärt als der Autor. Daumen hoch!
f0nzie
44

Wenn Sie nur eine Ausgabe wünschen, möchten Sie diese $(info)selbst verwenden. Sie können dies überall in einem Makefile tun und es wird angezeigt, wann diese Zeile ausgewertet wird:

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

Wird ausgegeben, VAR="<value of VAR>"wenn Prozesse in dieser Zeile ausgeführt werden. Dieses Verhalten ist sehr positionsabhängig, daher müssen Sie sicherstellen, dass die $(info)Erweiterung NACH allem erfolgt, was geändert werden könnte$(VAR) bereits erfolgt ist!

Eine allgemeinere Option besteht darin, eine spezielle Regel zum Drucken des Werts einer Variablen zu erstellen. Im Allgemeinen werden Regeln ausgeführt, nachdem Variablen zugewiesen wurden. Dies zeigt Ihnen also den Wert, der tatsächlich verwendet wird. (Es ist jedoch möglich, dass eine Regel eine Variable ändert .) Durch eine gute Formatierung wird klarer, auf was eine Variable eingestellt ist, und die $(flavor)Funktion zeigt an, um welche Art von Variable es sich handelt. Also in dieser Regel:

print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
  • $*erweitert sich zu dem Stamm, dass das % Muster in der Regel übereinstimmt.
  • $($*) wird auf den Wert der Variablen erweitert, deren Name durch gegeben ist $* .
  • Die [und definieren ]die variable Expansion klar. Sie könnten auch "und verwenden" oder ähnliches verwenden.
  • $(flavor $*)sagt Ihnen, um welche Art von Variable es sich handelt. HINWEIS: $(flavor) nimmt einen variablen Namen , und nicht seine Expansion. Wenn Sie also sagen make print-LDFLAGS, bekommen Sie $(flavor LDFLAGS), was Sie wollen.
  • $(info text)liefert Ausgabe. Machen Sie Drucke textauf dem Standard als Nebeneffekt der Erweiterung. Die Erweiterung von $(info)obwohl ist leer. Sie können sich das so vorstellen @echo, aber vor allem wird die Shell nicht verwendet, sodass Sie sich keine Gedanken über Regeln für das Zitieren von Shell machen müssen.
  • @trueist nur da, um einen Befehl für die Regel bereitzustellen. Ohne das wird auch make ausgegeben print-blah is up to date. Ich denke, es @truemacht klarer, dass es ein No-Op sein soll.

Wenn Sie es ausführen, bekommen Sie

$ make print-LDFLAGS
LDFLAGS is a recursive variable set to [-L/Users/...]
Jim Nasby
quelle
Dies gibt keine Antwort auf die Frage. Um einen Autor zu kritisieren oder um Klärung zu bitten, hinterlassen Sie einen Kommentar unter seinem Beitrag. Sie können jederzeit Ihre eigenen Beiträge kommentieren. Sobald Sie einen ausreichenden Ruf haben, können Sie jeden Beitrag kommentieren . - Aus dem Rückblick
Dragon Energy
Danke für die Bewertung. Ich verstehe, dass ich Kommentare abgeben kann, wenn ich genug Ruf habe, aber was soll ich in der Zwischenzeit tun? Ich glaube, meine Antwort bietet zusätzlichen Wert. Sollte ich sie nicht hinzufügen?
Jim Nasby
1
Ich würde vorschlagen, dies als eigenständige Antwort umzuschreiben, die mit der von Ihnen kommentierten Antwort konkurriert, anstatt sie als fehl am Platz befindlichen Kommentar zu dieser Antwort formulieren zu lassen.
Charles Duffy
@ JimNasby Ja, was Charles vorgeschlagen hat. Eigentlich könnte es als Anfang hilfreich sein, diesen Teil "Entschuldigung, nicht genug Mitarbeiter zum Kommentieren" einfach zu entfernen, da es sich um eine Art rote Fahne handelt. Wenn Sie daraus eine vollständige Antwort machen (können Sie alles bearbeiten, was Sie möchten), sollte es ganz gut funktionieren. Prost.
Dragon Energy
Ausgezeichnet - das ist jetzt eine viel bessere Antwort. :)
Charles Duffy
6

@echo $ (NDK_PROJECT_PATH) ist der gute Weg, dies zu tun. Ich glaube nicht, dass der Fehler von dort kommt. Im Allgemeinen tritt dieser Fehler auf, wenn Sie die Absicht falsch eingegeben haben: Ich denke, Sie haben Leerzeichen, in denen Sie eine Registerkarte haben sollten.


quelle
2
Ich habe '@echo $ (NDK_PROJECT_PATH)' ausprobiert. Es wird immer noch die Fehlermeldung "build-local.mk:102: *** fehlendes Trennzeichen. Stop." Angezeigt. Ich habe nur 1 Leerzeichen nach 'echo', es gibt kein Leerzeichen oder Tabulator vor '@echo'.
Michael
Sind Sie sicher, dass der Fehler von dieser Zeile stammt? Ist diese Zeile in der Regel? Sie muss wahrscheinlich eingerückt sein ...
6

In allen Versionen von makemüssen Befehlszeilen mit einem TAB (kein Leerzeichen) als erstem Zeichen in der Zeile eingerückt werden. Wenn Sie uns die gesamte Regel anstelle nur der beiden fraglichen Zeilen zeigen würden, könnten wir eine klarere Antwort geben, aber es sollte ungefähr so ​​aussehen:

myTarget: myDependencies
        @echo hi

Dabei muss das erste Zeichen in der zweiten Zeile TAB sein.

Verrückter Wissenschaftler
quelle
4

Lauf make -n ; Es zeigt Ihnen den Wert der Variablen.

Makefile ...

all:
        @echo $(NDK_PROJECT_PATH)

Befehl:

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

Ausgabe:

echo /opt/ndk/project
ivandcl
quelle
Dies ist jedoch ziemlich nützlich und ich benutze --just-printdas gleiche in Statt Ausführung
Fredrick Gauss
3

Dadurch makefilewird die Fehlermeldung "Fehlendes Trennzeichen" generiert:

all
    @echo NDK_PROJECT_PATH=$(NDK_PROJECT_PATH)

done:
        @echo "All done"

Es gibt eine Registerkarte vor dem @echo "All done"(obwohl die done:Regel und Aktion weitgehend überflüssig sind), aber nicht vor dem @echo PATH=$(PATH).

Das Problem ist, dass der Zeilenanfang allentweder einen Doppelpunkt :oder ein Gleichheitszeichen haben sollte= um anzuzeigen, dass es sich um eine Zielzeile oder eine Makrozeile handelt, und keine, sodass das Trennzeichen fehlt.

Die Aktion, die den Wert einer Variablen wiedergibt, muss einem Ziel zugeordnet sein, möglicherweise einem Dummy- oder PHONEY-Ziel. Und diese Ziellinie muss einen Doppelpunkt haben. Wenn Sie im Beispiel ein :Nach hinzufügenallmakefile und die führenden Leerzeichen in der nächsten Zeile durch eine Registerkarte ersetzen, wird es sanely arbeiten.

Sie haben wahrscheinlich ein analoges Problem in der Nähe von Zeile 102 im Original makefile. Wenn Sie vor den fehlgeschlagenen Echooperationen 5 nicht leere Zeilen ohne Kommentar angezeigt haben, ist es wahrscheinlich möglich, die Diagnose abzuschließen. Da die Frage jedoch im Mai 2013 gestellt wurde, ist es unwahrscheinlich, dass der Fehler makefilejetzt (August 2014) noch verfügbar ist, sodass diese Antwort nicht offiziell validiert werden kann. Es kann nur verwendet werden, um eine plausible Art und Weise zu veranschaulichen, in der das Problem aufgetreten ist.

Jonathan Leffler
quelle
3

Das Makefile muss nicht geändert werden.

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

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

Das Problem ist, dass Echo nur unter einem Ausführungsblock funktioniert. dh irgendetwas nach "xx:"

Alles über dem ersten Ausführungsblock ist also nur eine Initialisierung, sodass kein Ausführungsbefehl verwendet werden kann.

Erstellen Sie also einen Ausführungsblock

Andy
quelle
1

Dies kann generisch erfolgen und beim Debuggen eines komplexen Makefiles sehr nützlich sein. Nach der gleichen Technik wie in einer anderen Antwort beschrieben , können Sie Folgendes in jedes Makefile einfügen:

# 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

Dann können Sie einfach "make print" ausführen, um den Wert einer Variablen zu sichern:

$ make print CXXFLAGS
CXXFLAGS = -g -Wall
Idelic
quelle
0

Wenn Sie das Makefile selbst nicht ändern möchten, können --evalSie ein neues Ziel hinzufügen und dann das neue Ziel ausführen, z

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

Sie können das gewünschte TAB-Zeichen mit in die Befehlszeile einfügen CTRL-V, TAB

Beispiel Makefile von oben:

all: do-something

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

do-something:
        @echo "doing something"
        @echo "running tests $(TESTS)"
        @exit 1
Waten
quelle
Ich versuche, Ihr Skript auszuführen, aber ich make: *** missing separator. Stop.
erhalte
Ah, sorry, behoben. Fehlender Doppelpunkt am Ende des Ziels "Drucktests".
Wade
Eigentlich brauchen Sie keine Zeilenumbrüche und Tabulatoren, ein Semikolon wird ausreichen:make --eval='print-tests: ; @echo TESTS $(TESTS)' print-tests
Bobbogo
0

Wenn Sie Android Make (mka) verwenden, funktioniert dies @echo $(NDK_PROJECT_PATH)nicht und gibt Ihnen einen Fehler. *** missing separator. Stop." Verwenden Sie diese Antwort, wenn Sie versuchen, Variablen in Android Make zu drucken

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

das hat bei mir funktioniert

mjalil
quelle
0

Normalerweise erhalte ich ein Echo mit einem Fehler, wenn ich den Variablenwert sehen wollte. (Nur wenn Sie den Wert sehen wollten. Die Ausführung wird gestoppt.)

@echo $ (Fehler NDK_PROJECT_PATH = $ (NDK_PROJECT_PATH))

Abdul Jaleel
quelle