Ich versuche Folgendes zu tun. Es gibt ein Programm, nennen Sie es foo-bin
, das eine einzelne Eingabedatei aufnimmt und zwei Ausgabedateien generiert. Eine dumme Makefile-Regel dafür wäre:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
Dies bedeutet jedoch make
in keiner Weise, dass beide Ziele gleichzeitig generiert werden. Das ist in Ordnung, wenn es make
in Serie läuft , wird aber wahrscheinlich Probleme verursachen, wenn man es versucht make -j16
oder etwas ähnlich Verrücktes.
Die Frage ist, ob es eine Möglichkeit gibt, eine richtige Makefile-Regel für einen solchen Fall zu schreiben. Natürlich würde es eine DAG erzeugen, aber irgendwie gibt das GNU make-Handbuch nicht an, wie dieser Fall behandelt werden könnte.
Es kommt nicht in Frage, denselben Code zweimal auszuführen und nur ein Ergebnis zu generieren, da die Berechnung Zeit in Anspruch nimmt (denken Sie an Stunden). Die Ausgabe nur einer Datei wäre ebenfalls ziemlich schwierig, da sie häufig als Eingabe für GNUPLOT verwendet wird, das nicht weiß, wie nur ein Bruchteil einer Datendatei zu verarbeiten ist.
Antworten:
Ich würde es wie folgt lösen:
In diesem Fall "serialisiert" parallel make das Erstellen von a und b, aber da das Erstellen von b nichts bewirkt, dauert es keine Zeit.
quelle
file-b.out
es wie angegeben erstellt und dann auf mysteriöse Weise gelöscht wurde,make
kann es nicht neu erstellt werden, dafile-a.out
es weiterhin vorhanden ist. Irgendwelche Gedanken?file-a.out
funktioniert die obige Lösung gut. Dies deutet auf einen Hack hin: Wenn nur eines von a oder b vorhanden ist, sollten die Abhängigkeiten so angeordnet werden, dass die fehlende Datei als Ausgabe von angezeigt wirdinput.in
. Ein paar$(widcard...)
s,$(filter...)
s,$(filter-out...)
s usw. sollten den Trick machen. Pfui.foo-bin
offensichtlich zuerst eine der Dateien (mit einer Auflösung von Mikrosekunden). Sie müssen also sicherstellen, dass Sie die richtige Abhängigkeitsreihenfolge haben, wenn beide Dateien vorhanden sind.touch file-a.out
als zweiten Befehl nach demfoo-bin
Aufruf hinzufügen .touch file-b.out
in der ersten Regel einen zweiten Befehl hinzu und duplizieren Sie die Befehle in der zweiten Regel. Wenn eine Datei fehlt oder älter als istinput.in
, wird der Befehl nur einmal ausgeführt und beide Dateien werden mitfile-b.out
neueren Dateien neu generiertfile-a.out
.Der Trick besteht darin, eine Musterregel mit mehreren Zielen zu verwenden. In diesem Fall geht make davon aus, dass beide Ziele durch einen einzigen Aufruf des Befehls erstellt werden.
Dieser Unterschied in der Interpretation zwischen Musterregeln und normalen Regeln ist nicht gerade sinnvoll, aber in solchen Fällen nützlich und im Handbuch dokumentiert.
Dieser Trick kann für eine beliebige Anzahl von Ausgabedateien verwendet werden, sofern deren Namen eine gemeinsame Teilzeichenfolge haben
%
, damit sie übereinstimmen. (In diesem Fall ist der übliche Teilstring ".")quelle
make
ausführen , wird derselbe Befehl zweimal gleichzeitig ausgeführt.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(getestet mit GNU make 4.1).Make hat keine intuitive Möglichkeit, dies zu tun, aber es gibt zwei anständige Problemumgehungen.
Wenn die beteiligten Ziele einen gemeinsamen Stamm haben, können Sie zunächst eine Präfixregel verwenden (mit GNU make). Das heißt, wenn Sie die folgende Regel korrigieren möchten:
Sie könnten es so schreiben:
(Verwenden der Musterregelvariablen $ *, die für den übereinstimmenden Teil des Musters steht)
Wenn Sie für Nicht-GNU-Implementierungen portierbar sein möchten oder wenn Ihre Dateien nicht so benannt werden können, dass sie einer Musterregel entsprechen, gibt es einen anderen Weg:
Dies teilt make mit, dass input.in.intermediate nicht vorhanden ist, bevor make ausgeführt wird, sodass das Fehlen (oder der Zeitstempel) nicht dazu führt, dass foo-bin falsch ausgeführt wird. Und ob entweder file-a.out oder file-b.out oder beide veraltet sind (relativ zu input.in), foo-bin wird nur einmal ausgeführt. Sie können .SECONDARY anstelle von .INTERMEDIATE verwenden, wodurch make angewiesen wird, einen hypothetischen Dateinamen input.in.intermediate NICHT zu löschen. Diese Methode ist auch für parallele Make-Builds sicher.
Das Semikolon in der ersten Zeile ist wichtig. Es wird ein leeres Rezept für diese Regel erstellt, sodass Make weiß, dass wir file-a.out und file-b.out wirklich aktualisieren werden (danke @siulkiulki und anderen, die darauf hingewiesen haben).
quelle
-j1
Builds alles in Ordnung ist ). Wenn das Makefile unterbrochen wird und dieinput.in.intermediate
Datei in der Nähe bleibt, wird es sehr verwirrt.file-a.out file-b.out: input.in.intermediate
file-a.out file-b.out: input.in.intermediate ;
Dies basiert auf der zweiten Antwort von @ deemer, die nicht auf Musterregeln beruht, und behebt ein Problem, das bei verschachtelten Verwendungen der Problemumgehung aufgetreten ist.
Ich hätte dies als Kommentar zu @ deemers Antwort hinzugefügt, aber ich kann nicht, weil ich gerade dieses Konto erstellt habe und keinen Ruf habe.
Erläuterung: Das leere Rezept wird benötigt, damit Make die ordnungsgemäße Buchhaltung durchführen kann, um zu markieren
file-a.out
undfile-b.out
als neu erstellt zu werden. Wenn Sie noch ein anderes Zwischenziel haben, das davon abhängtfile-a.out
, wird Make entscheiden, das äußere Zwischenziel nicht zu erstellen, und behauptet:quelle
Nach GNU Make 4.3 (19. Januar 2020) können Sie "gruppierte explizite Ziele" verwenden. Ersetzen
:
durch&:
.Die NEWS-Datei des GNU Make lautet:
quelle
So mache ich es. Zuerst trenne ich immer die Voraussetzungen von den Rezepten. Dann in diesem Fall ein neues Ziel, um das Rezept zu machen.
Das erste Mal:
1. Wir versuchen, file-a.out zu erstellen, aber Dummy-a-and-b.out muss zuerst ausgeführt werden, damit das Dummy-a-and-b.out-Rezept ausgeführt wird.
2. Wir versuchen, file-b.out zu erstellen, Dummy-a-and-b.out ist auf dem neuesten Stand.
Das zweite und nachfolgende Mal:
1. Wir versuchen, file-a.out zu erstellen: Schauen Sie sich die Voraussetzungen an, normale Voraussetzungen sind aktuell, sekundäre Voraussetzungen fehlen und werden ignoriert.
2. Wir versuchen, file-b.out zu erstellen: Schauen Sie sich die Voraussetzungen an, normale Voraussetzungen sind aktuell, sekundäre Voraussetzungen fehlen und werden ignoriert.
quelle
file-b.out
make löschen , können Sie es nicht neu erstellen.Als Erweiterung der Antwort von @ deemer habe ich sie zu einer Funktion verallgemeinert.
Nervenzusammenbruch:
Entfernen Sie alle führenden / nachfolgenden Leerzeichen aus dem ersten Argument
Erstellen Sie ein variables Ziel mit einem eindeutigen Wert, der als Zwischenziel verwendet werden soll. In diesem Fall lautet der Wert .inter.file-a.out_file-b.out.
Erstellen Sie ein leeres Rezept für die Ausgaben mit dem eindeutigen Ziel als Abhängigkeit.
Deklarieren Sie das eindeutige Ziel als Zwischenprodukt.
Beenden Sie mit einem Verweis auf das eindeutige Ziel, damit diese Funktion direkt in einem Rezept verwendet werden kann.
Beachten Sie auch, dass eval hier verwendet wird, weil eval zu nichts erweitert wird, sodass die vollständige Erweiterung der Funktion nur das eindeutige Ziel ist.
Muss Kredit geben Atomic Regeln in GNU Make , die diese Funktion von inspiriert.
quelle
make -jN
Verwenden Sie, um die parallele Mehrfachausführung einer Regel mit mehreren Ausgaben mit zu verhindern.NOTPARALLEL: outputs
. In deinem Fall :quelle