So weisen Sie die Ausgabe eines Befehls einer Makefile-Variablen zu

225

Ich muss einige make-Regeln nur bedingt ausführen, wenn der installierte Python größer als eine bestimmte Version ist (z. B. 2.5).

Ich dachte, ich könnte so etwas wie Folgendes ausführen:

python -c 'import sys; print int(sys.version_info >= (2,5))'

und dann die Ausgabe ('1' wenn ok, '0' sonst) in einer ifeqmake-Anweisung verwenden.

In einem einfachen Bash-Shell-Skript ist es nur:

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

Aber das funktioniert in einem Makefile nicht.

Irgendwelche Vorschläge? Ich könnte jede andere sinnvolle Problemumgehung verwenden, um dies zu erreichen.

fortran
quelle
Seltsame Zurück-Ticks um die Befehlsarbeit zum Ausführen anderer Skripte für mich in einem Makefile. Könnte etwas anderes sein.
Leif Gruenwoldt

Antworten:

346

Verwenden Sie das shelleingebaute Make wie inMY_VAR=$(shell echo whatever)

me@Zack:~$make
MY_VAR IS whatever

me@Zack:~$ cat Makefile 
MY_VAR := $(shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)
Arkaitz Jimenez
quelle
34
Shell ist kein Standardbefehl zum Erstellen. Dies ist ein eingebautes GNU Make.
Dereckson
12
stackoverflow.com/a/2373111/12916 fügt einen wichtigen Hinweis zum Entkommen hinzu $.
Jesse Glick
6
Dieses einfache Beispiel funktioniert. Es funktioniert auch mit der Shell-Befehlspipeline. Aber es ist wichtig, dass Sie $$ verwenden, um $ im Shell-Befehl darzustellen
Sergey P. aka azure
28
Während die Frage ein wenig alt ist, ist es am besten, MY_VAR: = $ (Shell ...) zu machen. Andernfalls wird $ (Shell ...) jedes Mal, wenn MY_VAR ausgewertet wird, erneut ausgeführt.
Russ Schultz
Ich hatte ein Leerzeichen zwischen Shell und den öffnenden Klammern und erst nachdem ich das Leerzeichen entfernt hatte, gab mein Makefile-Ausgabetext
Peterchaula
29

Das Einwickeln der Aufgabe in eine evalfunktioniert für mich.

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)
Dave
quelle
6
"Hinweis: Das @true hier verhindert, dass Make denkt, dass nichts zu tun ist." Ähm, dafür .PHONY always make these targetsist es da.
underscore_d
Danke dir! Dies hilft mir, "seltsame Bash" in Makefile
Nam G VU zu
19

Hier ist ein etwas komplizierteres Beispiel mit Rohrleitungen und Variablenzuweisung innerhalb des Rezepts:

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)
Francesco Casula
quelle
2
Für den Fall, dass es jemals nützlich sein sollte, verwende ich einen ähnlichen Ansatz, um PODNAMEden Einsatznamen zu ermitteln:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter
Die Syntax verwirrte mich für eine Sekunde, bis mir klar wurde, dass Sie sowohl die Shell-integrierte Bewertung (in der Docker- Env -Zeile) als auch die Make-Funktion Bewertung (in der nächsten Zeile) verwenden.
Brian Gordon
17

Ich schreibe eine Antwort, um die Sichtbarkeit der tatsächlichen Syntax zu erhöhen, die das Problem löst. Leider kann das, was jemand als trivial ansieht, für jemanden, der nach einer einfachen Antwort auf eine vernünftige Frage sucht, zu einem erheblichen Problem werden.

Fügen Sie Folgendes in die Datei "Makefile" ein.

MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

Das Verhalten, das Sie sehen möchten, ist das folgende (vorausgesetzt, Sie haben kürzlich Python installiert).

make
MY_VAR IS 1

Wenn Sie den obigen Text kopieren und in das Makefile einfügen, erhalten Sie diesen? Wahrscheinlich nicht. Sie werden wahrscheinlich einen Fehler wie den hier gemeldeten erhalten:

Makefile: 4: *** fehlendes Trennzeichen. Halt

Warum: Obwohl ich persönlich einen echten Tab verwendet habe, konvertiert Stack Overflow (der versucht, hilfreich zu sein) meinen Tab in eine Reihe von Leerzeichen. Sie, frustrierter Internet-Bürger, kopieren dies jetzt und denken, dass Sie jetzt denselben Text haben, den ich verwendet habe. Der Befehl make liest nun die Leerzeichen und stellt fest, dass der Befehl "all" falsch formatiert ist. Kopieren Sie also den obigen Text, fügen Sie ihn ein und konvertieren Sie das Leerzeichen vor "@echo" in eine Registerkarte. Dieses Beispiel sollte hoffentlich endlich für Sie funktionieren.

AlanSE
quelle
Hängt davon ab, in welchen Editor Sie einfügen. Ich habe gerade kopiert und in einen Eclipse-Makefile-Editor eingefügt und eine führende Registerkarte erhalten (je nach Bedarf).
Technophile
Oh, daran habe ich nicht gedacht. Atom hier.
AlanSE
11

Vorsicht vor solchen Rezepten

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

Es macht zwei Dinge falsch. Die erste Zeile im Rezept wird in einer von der zweiten Zeile getrennten Shell-Instanz ausgeführt. Die Variable geht in der Zwischenzeit verloren. Das zweite, was falsch ist, ist, dass das $nicht entkommen ist.

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

Beide Probleme wurden behoben und die Variable ist verwendbar. Der Backslash kombiniert beide Zeilen, um in einer einzigen Shell ausgeführt zu werden. Daher funktioniert die Einstellung der Variablen und das Lesen der variablen Nachwörter.

Mir ist klar, dass im ursprünglichen Beitrag gesagt wurde, wie die Ergebnisse eines Shell-Befehls in eine MAKE-Variable übernommen werden, und diese Antwort zeigt, wie man sie in eine Shell-Variable umwandelt. Aber andere Leser können davon profitieren.

Eine letzte Verbesserung: Wenn der Verbraucher erwartet, dass eine "Umgebungsvariable" festgelegt wird, müssen Sie sie exportieren.

my_shell_script
    echo $MY_ID

würde dies im Makefile brauchen

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_shell_script;

Hoffe das hilft jemandem. Im Allgemeinen sollte man es vermeiden, echte Arbeit außerhalb von Rezepten zu leisten, denn wenn jemand das Makefile mit der Option '--dry-run' verwendet, um nur zu sehen, was es tun wird, hat es keine unerwünschten Nebenwirkungen. Jeder $(shell)Aufruf wird zur Kompilierungszeit ausgewertet, und es könnte versehentlich echte Arbeit geleistet werden. Überlassen Sie die eigentliche Arbeit, wie das Generieren von IDs, nach Möglichkeit dem Inneren der Rezepte.

Juraj
quelle
9

Mit GNU Make können Sie die Ausgabe beliebiger Befehlszeilenaufrufe verwenden shellund evalspeichern, ausführen und zuweisen. Der Unterschied zwischen dem folgenden Beispiel und denen, die verwendet werden, :=besteht darin, dass die :=Zuweisung einmal (wenn sie auftritt) und für alle erfolgt. Rekursiv erweiterte Variablen, mit =denen gesetzt wird, sind etwas "fauler"; Verweise auf andere Variablen bleiben bestehen, bis auf die Variable selbst verwiesen wird, und die nachfolgende rekursive Erweiterung findet jedes Mal statt, wenn auf die Variable verwiesen wird , was wünschenswert ist, um "konsistente, aufrufbare Snippets" zu erstellen. Weitere Informationen finden Sie im Handbuch zum Einstellen von Variablen .

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)
Mitchell Tracy
quelle
Sie haben die erste nette Erklärung, die mir je begegnet ist. Danke dir. Eine Sache, die hinzugefügt werden muss, ist, dass wenn $(SET_ID)eine ifKlausel lebt , die falsch ist, sie immer noch aufgerufen wird .
Roman
Es wird immer noch aufgerufen, da die if stmts zur Makefile-Kompilierungszeit und nicht zur Laufzeit ausgewertet werden. Fügen Sie zur Laufzeit spezifische Anweisungen in Rezepte ein. Wenn sie bedingt sind, fügen Sie if stmts, geschrieben in bash / shell, als Teil des Rezepts hinzu.
Juraj
0

Im folgenden Beispiel habe ich den Pfad des Makefile-Ordners in gespeichert LOCAL_PKG_DIRLOCAL_PKG_DIR Zielen und diese dann in Zielen verwendet.

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

.PHONY: print
print:
    @echo $(LOCAL_PKG_DIR)

Terminalausgang:

$ make print
/home/amrit/folder
Amritpal Singh
quelle