Definieren Sie make variable zur Ausführungszeit der Regel

208

In meiner GNUmake-Datei möchte ich eine Regel haben, die ein temporäres Verzeichnis verwendet. Beispielsweise:

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

Wie oben beschrieben, erstellt die obige Regel das temporäre Verzeichnis zum Zeitpunkt des Analysierens der Regel . Dies bedeutet, dass viele temporäre Verzeichnisse erstellt werden, auch wenn ich nicht die ganze Zeit rumgefahren bin. Ich möchte vermeiden, dass mein / tmp mit nicht verwendeten temporären Verzeichnissen übersät ist.

Gibt es eine Möglichkeit, die Variable nur zu definieren, wenn die Regel ausgelöst wird, und nicht, wenn sie definiert wird?

Mein Hauptgedanke ist es, mktemp und tar in ein Shell-Skript zu kopieren, aber das scheint etwas unansehnlich.

Emil Setz dich
quelle

Antworten:

321

In Ihrem Beispiel wird die TMPVariable festgelegt (und das temporäre Verzeichnis erstellt), wenn die Regeln für out.tarausgewertet werden. Um das Verzeichnis nur dann zu erstellen, wenn out.tares tatsächlich ausgelöst wird, müssen Sie die Verzeichniserstellung in die folgenden Schritte verschieben:

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

Die Funktion eval wertet eine Zeichenfolge so aus, als ob sie manuell in das Makefile eingegeben worden wäre. In diesem Fall wird die TMPVariable auf das Ergebnis des shellFunktionsaufrufs gesetzt.

bearbeiten (als Antwort auf Kommentare):

Um eine eindeutige Variable zu erstellen, haben Sie folgende Möglichkeiten:

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

Dies würde der Variablen den Namen des Ziels (in diesem Fall out.tar) voranstellen und eine Variable mit dem Namen erzeugen out.tar_TMP. Hoffentlich reicht das aus, um Konflikte zu vermeiden.

James
quelle
2
Cool ein paar Klarstellungen ... das wird TMP nicht auf dieses Ziel ausweiten, oder? Wenn es also andere Regeln gibt, die ihre eigene Verwendung von $ (TMP) haben (möglicherweise parallel zu -j), kann es zu Konflikten kommen? Ist das @echo überhaupt notwendig? Scheint, Sie könnten es einfach weglassen.
Emil Sit
3
Scheint den Trick zu machen (obwohl ein bisschen undurchsichtig für den durchschnittlichen Nicht-Make-Guru :-) Danke!
Emil Sit
29
Seien Sie vorsichtig mit dieser Lösung! $(eval $@_TMP := $(shell mktemp -d))wird passieren, wenn das Makefile zum ersten Mal ausgewertet wird, nicht in der Reihenfolge der Regelprozeduren. Mit anderen Worten, $(eval ...)passiert früher als Sie denken. Während dies für dieses Beispiel in Ordnung sein könnte, verursacht diese Methode Probleme bei einigen sequentiellen Operationen.
JamesThomasMoon1979
1
@ JamesThomasMoon1979 wissen Sie, wie Sie diese Einschränkungen überwinden können, z. B. einige Variablen bewerten, die das Ergebnis von Schritten sein sollten, die früher in der Regel ausgeführt wurden?
Vadim Kotov
2
Beantwortung meiner eigenen Frage: Die Problemumgehung für mich bestand darin, Dateien während der Ausführung der ersten Regel zu erstellen und zu versuchen, sie im ersten Schritt der zweiten Regel auszuwerten.
Vadim Kotov
63

Eine relativ einfache Möglichkeit besteht darin, die gesamte Sequenz als Shell-Skript zu schreiben.

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

Ich habe hier einige verwandte Tipps zusammengefasst: https://stackoverflow.com/a/29085684/86967

kein Balken
quelle
3
Dies ist definitiv die einfachste und daher beste Antwort (vermeiden @und evalund machen den gleichen Job). Beachten Sie, dass in Ihrer Ausgabe $TMP(z. B. tar -C $TMP ...) angezeigt wird, obwohl der Wert korrekt an den Befehl übergeben wurde.
Karl Richter
So wird es normalerweise gemacht; Ich hoffe, die Leute sehen diese Antwort, weil in der Praxis niemand die ganze Anstrengung des Akzeptierten durchmacht.
pmos
Was ist, wenn Sie Shell-spezifische Befehle verwenden? In diesem Fall müssen Shell-Befehle in derselben Shell-Sprache sein wie derjenige, der make aufruft, oder? Ich hatte ein Makefile mit Shebang gesehen.
Ptitpion
@ptitpion: Möglicherweise möchten Sie SHELL := /bin/bashin Ihrem Makefile auch BASH-spezifische Funktionen aktivieren.
Nobar
31

Eine andere Möglichkeit besteht darin, separate Zeilen zu verwenden, um Make-Variablen einzurichten, wenn eine Regel ausgelöst wird.

Hier ist zum Beispiel ein Makefile mit zwei Regeln. Wenn eine Regel ausgelöst wird, erstellt sie ein temporäres Verzeichnis und setzt TMP auf den Namen des temporären Verzeichnisses.

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}

Das Ausführen des Codes führt zu dem erwarteten Ergebnis:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
user3124434
quelle
Zur Erinnerung: GNU make verfügt über eine Syntax für reine Bestellvoraussetzungen, die möglicherweise zur Ergänzung dieses Ansatzes erforderlich sind.
Anol
11
Seien Sie vorsichtig mit dieser Lösung! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)und ruleB: TMP = $(shell mktemp -d testruleB_XXXX)wird passieren, wenn das Makefile zum ersten Mal ausgewertet wird. Mit anderen Worten, ruleA: TMP = $(shell ...passiert früher als Sie denken. Während dies in diesem speziellen Fall möglicherweise funktioniert, verursacht diese Methode bei einigen sequentiellen Operationen Probleme.
JamesThomasMoon1979
1

Ich mag keine "Nicht" -Antworten, aber ... nicht.

makeDie Variablen von sind global und sollen während der "Parsing" -Stufe des Makefiles ausgewertet werden, nicht während der Ausführungsphase.

In diesem Fall folgen Sie der Antwort von @ nobar , solange sich die Variable auf einem einzelnen Ziel befindet und machen Sie sie zu einer Shell-Variablen.

Auch zielspezifische Variablen werden von anderen make-Implementierungen als schädlich angesehen: kati , Mozilla pymake . Aufgrund dessen kann ein Ziel unterschiedlich erstellt werden, je nachdem, ob es eigenständig erstellt wurde oder als Abhängigkeit eines übergeordneten Ziels mit einer zielspezifischen Variablen. Und Sie werden nicht wissen, wie es war , weil Sie nicht wissen, was bereits gebaut ist.

Victor Sergienko
quelle