Come assegnare l'output di un comando a una variabile Makefile


225

Ho bisogno di eseguire alcune regole di condizionale, solo se il Python installato è maggiore di una certa versione (diciamo 2.5).

Pensavo di poter fare qualcosa come eseguire:

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

e quindi usando l'output ('1' se ok, '0' altrimenti) in ifequn'istruzione make.

In un semplice script bash shell è solo:

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

ma questo non funziona in un Makefile.

Eventuali suggerimenti? Potrei usare qualsiasi altra soluzione ragionevole per raggiungere questo obiettivo.


Strani segni di spunta intorno al lavoro di comando per l'esecuzione di altri script per me in un Makefile. Potrebbe essere qualcos'altro.
Leif Gruenwoldt,

Risposte:


346

Usa il shellcomando Crea come 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)

34
shell non è un comando standard incorporato Make. Questo è un GNU Make integrato.
Dereckson,

12
stackoverflow.com/a/2373111/12916 aggiunge una nota importante sull'evasione $.
Jesse Glick,

6
Questo semplice esempio funziona. Funziona anche con pipeline di comandi shell. Ma è essenziale usare $$ per rappresentare $ nel comando shell
Sergey P. aka azure

28
Mentre la domanda è leggermente vecchia, è meglio fare MY_VAR: = $ (shell ...), altrimenti ogni volta che MY_VAR viene valutato, eseguirà di nuovo $ (shell ...).
Russ Schultz,

Avevo uno spazio tra la shell e le parentesi aperte e solo dopo aver rimosso lo spazio il testo del mio makefile era in uscita
peterchaula,

29

Concludere l'incarico in an evalfunziona per me.

# 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)

6
"Nota: @true qui impedisce a Make di pensare che non c'è nulla da fare." Uhm, questo è ciò che .PHONY always make these targetsserve.
underscore_d

Grazie! Questo mi aiuta a bypassare "weird bash" nel makefile
Nam G VU

19

Ecco un esempio un po 'più complicato di tubazioni e assegnazione delle variabili all'interno della ricetta:

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)

2
Nel caso in cui fosse utile, sto usando un approccio un po 'simile per ottenere in PODNAMEbase al nome della distribuzione:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter,

La sintassi mi ha confuso per un secondo fino a quando ho capito che stavi usando sia la shell builtin eval (sulla linea docker-env) che la funzione make eval (sulla riga successiva).
Brian Gordon,

17

Sto scrivendo una risposta per aumentare la visibilità della sintassi effettiva che risolve il problema. Sfortunatamente, ciò che qualcuno potrebbe considerare banale può diventare un mal di testa molto significativo per qualcuno che cerca una risposta semplice a una domanda ragionevole.

Inserisci quanto segue nel file "Makefile".

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

all:
    @echo MY_VAR IS $(MY_VAR)

Il comportamento che vorresti vedere è il seguente (supponendo che tu abbia installato Python recente).

make
MY_VAR IS 1

Se copi e incolli il testo sopra nel Makefile, otterrai questo? Probabilmente no. Probabilmente riceverai un errore come quello riportato qui:

makefile: 4: *** mancante separatore. Fermare

Perché: perché sebbene io abbia usato personalmente una scheda originale, Stack Overflow (cercando di essere utile) converte la mia scheda in un numero di spazi. Tu, frustrato cittadino di Internet, ora copia questo, pensando che ora hai lo stesso testo che ho usato. Il comando make ora legge gli spazi e scopre che il comando "all" non è formattato correttamente. Quindi copia il testo sopra, incollalo e poi converti lo spazio bianco prima di "@echo" in una scheda, e questo esempio dovrebbe, alla fine, si spera, funzionare per te.


Dipende da quale editor stai incollando. Ho appena copiato e incollato in un editor Eclipse Makefile e ho ottenuto una scheda iniziale (come richiesto).
Technophile,

Oh, non ci ho pensato. Atom qui.
AlanSE,

11

Fai attenzione a ricette come questa

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

Fa due cose sbagliate. La prima riga della ricetta viene eseguita in un'istanza di shell separata dalla seconda riga. La variabile si perde nel frattempo. La seconda cosa che non va è che $non è sfuggito.

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

Entrambi i problemi sono stati risolti e la variabile è utilizzabile. La barra rovesciata combina entrambe le righe per l'esecuzione in una singola shell, quindi l'impostazione della variabile e la lettura delle parole secondarie della variabile funzionano.

Mi rendo conto che il post originale diceva come ottenere i risultati di un comando shell in una variabile MAKE, e questa risposta mostra come inserirlo in una variabile shell. Ma altri lettori potrebbero trarne beneficio.

Un miglioramento finale, se il consumatore prevede di impostare una "variabile d'ambiente", è necessario esportarla.

my_shell_script
    echo $MY_ID

avrebbe bisogno di questo nel makefile

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

Spero che aiuti qualcuno. In generale, si dovrebbe evitare di fare qualsiasi lavoro reale al di fuori delle ricette, perché se qualcuno usa il makefile con l'opzione '--dry-run', per VEDERE solo ciò che farà, non avrà effetti collaterali indesiderati. Ogni $(shell)chiamata viene valutata al momento della compilazione e alcuni lavori reali potrebbero essere eseguiti accidentalmente. Meglio lasciare il vero lavoro, come generare ID, all'interno delle ricette quando possibile.


9

Con GNU Make, è possibile utilizzare shelle evalarchiviare, eseguire e assegnare output da invocazioni di riga di comando arbitrarie. La differenza tra l'esempio che segue e quelli che usano :=è l' :=assegnazione avviene una volta (quando viene incontrata) e per tutti. Le variabili ricorsivamente espanse impostate con =sono un po 'più "pigre"; i riferimenti ad altre variabili rimangono fino a quando non si fa riferimento alla variabile stessa e la successiva espansione ricorsiva ha luogo ogni volta che si fa riferimento alla variabile , il che è desiderabile per rendere "coerenti, richiamabili, frammenti". Vedere il manuale sull'impostazione delle variabili per ulteriori informazioni.

# 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)

Hai la prima bella spiegazione che ho incontrato. Grazie. Anche se una cosa da aggiungere è che se $(SET_ID)la vita all'interno di una ifclausola di che è falsa , allora esso è ancora chiamato .
Romano

Viene ancora chiamato perché gli stmts vengono valutati in fase di compilazione makefile e non in fase di esecuzione. Per istruzioni specifiche di runtime, inserirle nelle ricette. Se sono condizionali, aggiungi if stmts, scritto in bash / shell, come parte della ricetta.
Juraj,

0

Nell'esempio seguente, ho archiviato il percorso della cartella Makefile LOCAL_PKG_DIRe quindi utilizzo la LOCAL_PKG_DIRvariabile nelle destinazioni.

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

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

Uscita terminale:

$ make print
/home/amrit/folder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.