2010-06-04 6 views
81

Ich versuche folgendes zu tun. Es gibt ein Programm, nennen Sie es foo-bin, das eine einzelne Eingabedatei einnimmt und zwei Ausgabedateien generiert. Eine stumme Makefile Regel hierfür wäre:GNU Makefile-Regel, die ein paar Ziele aus einer einzigen Quelldatei erzeugt

file-a.out file-b.out: input.in 
    foo-bin input.in file-a.out file-b.out 

Dies ist jedoch nicht make in keiner Weise sagen, dass beide Ziele gleichzeitig erzeugt werden. Das ist in Ordnung, wenn make in Serie ausgeführt wird, aber wahrscheinlich Probleme verursachen wird, wenn man make -j16 oder etwas Ähnliches versucht.

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 generieren, aber irgendwie die GNU make manual nicht angeben, wie dieser Fall behandelt werden könnte.

Das Ausführen des gleichen Codes zweimal und das Generieren nur eines Ergebnisses kommt nicht in Frage, weil die Berechnung Zeit benötigt (Denken: Stunden). Die Ausgabe von nur einer Datei wäre auch ziemlich schwierig, da sie häufig als Eingabe für GNUPLOT verwendet wird, die nicht weiß, wie sie nur mit einem Bruchteil einer Datendatei umgehen soll.

Antwort

7

Ich würde es wie folgt lösen:

file-a.out: input.in 
    foo-bin input.in file-a.out file-b.out 

file-b.out: file-a.out 
    #do nothing 
    noop 

In diesem Fall parallel machen ‚serialize‘ a und b zu schaffen, aber da b Erstellung nicht alles tun, dauert es keine Zeit.

+12

Eigentlich gibt ein Problem mit diesem ist. Wenn 'file-b.out' wie angegeben erstellt und dann mysteriös gelöscht wurde, kann' make' es nicht wiederherstellen, da 'file-a.out' immer noch vorhanden ist. Irgendwelche Gedanken? – makesaurus

+0

Wenn Sie mysteriös 'file-a.out' löschen, funktioniert die obige Lösung gut. Dies deutet auf einen Hack hin: Wenn nur einer von a oder b vorhanden ist, sollten die Abhängigkeiten so angeordnet werden, dass fehlende Dateien als Ausgabe von "input.in" angezeigt werden. Ein paar '$ (widcard ...)' s, '$ (filter ...)' s, '$ (filter-out ...)' s, sollten den Trick machen. Pfui. – bobbogo

+3

P.S. 'foo-bin' erstellt offensichtlich zuerst eine der Dateien (mit Mikrosekundenauflösung), so dass Sie sicherstellen müssen, dass Sie die richtige Reihenfolge der Abhängigkeiten haben, wenn beide Dateien existieren. – bobbogo

110

Der Trick besteht darin, eine Musterregel mit mehreren Zielen zu verwenden. In diesem Fall wird make davon ausgehen, dass beide Ziele durch einen einzigen Aufruf des Befehls erstellt werden.

 
all: file-a.out file-b.out 
file-a%out file-b%out: input.in 
    foo-bin input.in file-a$*out file-b$*out 

Dieser Unterschied in der Interpretation zwischen Musterregeln und normalen Regeln genau macht keinen Sinn, aber es ist nützlich für Fälle wie diese, und es ist im Handbuch dokumentiert.

Dieser Trick kann für eine beliebige Anzahl von Ausgabedateien verwendet werden, solange ihre Namen eine gemeinsame Teilzeichenfolge für die % entsprechen. (In diesem Fall ist der gemeinsame Teilstring ".")

+1

Das ist eigentlich nicht korrekt. Ihre Regel gibt an, dass Dateien, die mit diesen Mustern übereinstimmen, mit dem angegebenen Befehl aus input.in erstellt werden sollen, aber nirgends heißt es, dass sie gleichzeitig erstellt werden. Wenn Sie es parallel ausführen, wird 'make' den gleichen Befehl zweimal gleichzeitig ausführen. – makesaurus

+37

Bitte versuchen Sie es, bevor Sie sagen, es funktioniert nicht ;-) Die GNU make manual sagt: "Muster Regeln können mehr als ein Ziel haben. Im Gegensatz zu normalen Regeln, dies nicht so viele verschiedene Regeln mit den gleichen Voraussetzungen und Wenn eine Musterregel mehrere Ziele hat, weiß make, dass die Befehle der Regel für die Erstellung aller Ziele verantwortlich sind. Die Befehle werden nur einmal ausgeführt, um alle Ziele zu erstellen. " http://www.gnu.org/software/make/manual/make.html#Pattern-Intro – slowdog

+0

Das ist ein cleverer Trick. Ich hatte nicht daran gedacht, ein '%' auf der RHS einer Musterregel zu benötigen. –

3

So mache ich es. Zuerst separiere ich immer Vorbestellungen von den Rezepten. Dann in diesem Fall ein neues Ziel für das Rezept.

all: file-a.out file-b.out #first rule 

file-a.out file-b.out: input.in 

file-a.out file-b.out: dummy-a-and-b.out 

.SECONDARY:dummy-a-and-b.out 
dummy-a-and-b.out: 
    echo creating: file-a.out file-b.out 
    touch file-a.out file-b.out 

Das erste Mal: ​​
1. Wir versuchen Datei-a.out, aber Dummy-a-and-B.OUT getan werden muss bauen zunächst so läuft machen die Dummy-a-und-b. aus Rezept.
2. Wir versuchen, Datei-b.out zu bauen, Dummy-a-und-b.out ist auf dem neuesten Stand.

Die zweite und folgende Zeit:
1. Wir versuchen, file-a.out zu erstellen: make sieht an Voraussetzungen, normale Voraussetzungen sind auf dem neuesten Stand, sekundäre Voraussetzungen fehlen so ignoriert.
2. Wir versuchen, file-b.out zu erstellen: make schaut auf Voraussetzungen, normale Voraussetzungen sind auf dem neuesten Stand, sekundäre Voraussetzungen fehlen, werden also ignoriert.

+1

Das ist kaputt. Wenn Sie 'file-b.out' löschen, gibt es keine Möglichkeit, es neu zu erstellen. – bobbogo

+0

hat alle hinzugefügt: file-a.out file-b.out als erste Regel. Als ob file-b.out entfernt wurde und make ohne Ziel in der Befehlszeile ausgeführt wurde, wurde es nicht neu erstellt. –

+0

@bobbogo Behoben: siehe obigen Kommentar. –

39

Make hat keine intuitive Möglichkeit, dies zu tun, aber es gibt zwei gute Workarounds.

Erstens, wenn die beteiligten Ziele einen gemeinsamen Stamm haben, können Sie eine Präfixregel (mit GNU make) verwenden.Das heißt, wenn Sie die folgende Regel beheben wollte:

object.out1 object.out2: object.input 
    foo-bin object.input object.out1 object.out2 

Sie können schreiben Sie es auf diese Weise:

%.out1 %.out2: %.input 
    foo-bin $*.input $*.out1 $*.out2 

(Unter Verwendung der Muster-Regel Variable $ *, die für den angepassten Teil steht der das Muster)

wenn Sie portable zu nicht-GNU-Implementierungen machen zu wollen, oder wenn Sie Ihre Dateien nicht genannt werden kann, eine Musterregel übereinstimmen, gibt es eine andere Art und Weise:

file-a.out file-b.out: input.in.intermediate 

.INTERMEDIATE: input.in.intermediate 
input.in.intermediate: input.in 
    foo-bin input.in file-a.out file-b.out 

Dies sagt make, dass input.in.mediate nicht existiert, bevor make ausgeführt wird, so dass sein Fehlen (oder sein Zeitstempel) nicht dazu führt, dass foo-bin falsch ausgeführt wird. Und ob 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 NOT den hypothetischen Dateinamen input.in.intermediate nicht löschen soll. Diese Methode ist auch für parallele make-Builds sicher.

+6

Nizza, looks wie. INTERMEDIATE Ansatz funktioniert gut. Siehe auch [Automake Artikel] (http://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html#Multiple-Outputs) beschreibt das Problem (aber sie versuchen, es in einem tragbaren zu lösen Weg). – grwlf

+2

Dies ist die beste Antwort, die ich dazu sah. Interessant könnten auch die anderen speziellen Ziele sein: https://www.gnu.org/software/make/manual/html_node/Special-Targets.html –

+2

@deemer Das funktioniert bei OSX nicht für mich. (GNU Make 3.81) –

2

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.

file-a.out file-b.out: input.in.intermediate 
    @# Empty recipe to propagate "newness" from the intermediate to final targets 

.INTERMEDIATE: input.in.intermediate 
input.in.intermediate: input.in 
    foo-bin input.in file-a.out file-b.out 

Ich würde dies als Kommentar hinzugefügt haben @ deemer Antwort, aber ich kann nicht, weil ich gerade dieses Konto erstellt und haben keinen guten Ruf.

Erläuterung: Das leere Rezept wird benötigt, damit Make die korrekte Buchhaltung ausführen kann, um file-a.out und file-b.out als neu zu markieren. Wenn Sie noch ein weiteres Zwischenziel haben, die auf file-a.out abhängt, dann machen wählen, um nicht die äußere Zwischen bauen und behauptete:

No recipe for 'file-a.out' and no prerequisites actually changed. 
No need to remake target 'file-a.out'. 
Verwandte Themen