Definisci make variabile al momento dell'esecuzione della regola


209

Nel mio GNUmakefile, vorrei avere una regola che utilizza una directory temporanea. Per esempio:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

Come scritto, la regola sopra crea la directory temporanea nel momento in cui la regola viene analizzata . Ciò significa che, anche se non lo capisco sempre, vengono create molte directory temporanee. Vorrei evitare che il mio / tmp sia disseminato di directory temporanee inutilizzate.

C'è un modo per far sì che la variabile venga definita solo quando la regola viene attivata, al contrario di quando viene definita?

Il mio pensiero principale è di scaricare mktemp e tar in uno script di shell, ma sembra un po 'sgradevole.

Risposte:


322

Nel tuo esempio, la TMPvariabile viene impostata (e viene creata la directory temporanea) ogni volta cheout.tar vengono valutate le regole per . Per creare la directory solo quando out.tarviene effettivamente attivato, è necessario spostare la creazione della directory nei passaggi seguenti:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

La funzione eval valuta una stringa come se fosse stata digitata manualmente nel makefile. In questo caso, imposta la TMPvariabile sul risultato della shellchiamata di funzione.

modifica (in risposta ai commenti):

Per creare una variabile unica, è possibile effettuare le seguenti operazioni:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

Ciò anteporrebbe il nome del target (out.tar, in questo caso) alla variabile, producendo una variabile con il nome out.tar_TMP. Speriamo che sia abbastanza per prevenire i conflitti.


2
Raffreddare alcuni chiarimenti ... questo non mirerà TMP a questo obiettivo, vero? Quindi se ci sono altre regole che hanno il loro uso $ (TMP) (possibilmente in parallelo con -j), ci possono essere conflitti? Inoltre, @echo è addirittura necessario? Sembra che potresti lasciarlo fuori.
Emil Sit,

3
Sembra fare il trucco (anche se un po 'opaco per il guru medio non-Make :-) Grazie!
Emil Sit,

29
Stai attento con questa soluzione! $(eval $@_TMP := $(shell mktemp -d))accadrà quando il Makefile viene valutato per la prima volta non nell'ordine delle procedure della regola. In altre parole, $(eval ...)succede prima di quanto pensi. Anche se questo potrebbe andare bene per questo esempio, questo metodo causerà problemi per alcune operazioni sequenziali.
JamesThomasMoon1979,

1
@ JamesThomasMoon1979 sai come superare tali limiti, ad esempio valutare alcune variabili che dovrebbero essere il risultato di passaggi eseguiti in precedenza nella regola?
Vadim Kotov,

2
Rispondere alla mia domanda: la soluzione alternativa è stata la creazione di file durante l'esecuzione della prima regola e il tentativo di valutarli nel primo passaggio della seconda regola.
Vadim Kotov,

63

Un modo relativamente semplice per farlo è scrivere l'intera sequenza come uno script di shell.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

Ho consolidato alcuni suggerimenti correlati qui: https://stackoverflow.com/a/29085684/86967


3
Questo è sicuramente la risposta più semplice e quindi più (evitando @e evale facendo lo stesso lavoro). Si noti che nell'output viene visualizzato $TMP(ad es. tar -C $TMP ...) Sebbene il valore sia passato correttamente al comando.
Karl Richter,

Questo è come viene normalmente fatto; Spero che la gente veda questa risposta perché in pratica nessuno passa attraverso tutto lo sforzo di quello accettato.
pmos

Cosa succede se si utilizzano comandi specifici della shell? In tal caso i comandi della shell devono essere nella stessa lingua della shell di quella che chiama, giusto? Avevo visto un po 'di makefile con Shebang.
ptitpion,

@ptitpion: SHELL := /bin/bashnel tuo makefile potresti anche abilitare le funzionalità specifiche di BASH.
nobar

31

Un'altra possibilità è quella di utilizzare linee separate per impostare le variabili Make quando viene generata una regola.

Ad esempio, ecco un makefile con due regole. Se una regola viene attivata, crea una directory temporanea e imposta TMP sul nome della directory temporanea.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

L'esecuzione del codice produce il risultato previsto:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

Proprio come promemoria, GNU make ha una sintassi per i prerequisiti di solo ordine che possono essere necessari per integrare questo approccio.
anol,

11
Stai attento con questa soluzione! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)e ruleB: TMP = $(shell mktemp -d testruleB_XXXX)accadrà alla prima valutazione del Makefile. In altre parole, ruleA: TMP = $(shell ...succede prima di quanto pensi. Mentre questo potrebbe funzionare per questo caso particolare, questo metodo causerà problemi per alcune operazioni sequenziali.
JamesThomasMoon1979,

1

Non mi piacciono le risposte "Non", ma ... no.

makeLe variabili sono globali e dovrebbero essere valutate durante la fase di "analisi" del makefile, non durante la fase di esecuzione.

In questo caso, fino a quando la variabile locale in un singolo target, segui la risposta di @ nobar e trasformala in una variabile di shell.

Anche le variabili specifiche del target sono considerate dannose da altre implementazioni di make: kati , Mozilla pymake . A causa loro, una destinazione può essere costruita in modo diverso a seconda che sia costruita autonomamente o come dipendenza di una destinazione principale con una variabile specifica della destinazione. E non saprai da che parte fosse , perché non sai cosa è già stato costruito.

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.