So verhindern Sie, dass bei Verwendung von mkdir in einem Makefile der Fehler "Verzeichnis existiert bereits" auftritt

109

Ich muss ein Verzeichnis in meinem Makefile generieren und möchte nicht immer wieder den Fehler "Verzeichnis existiert bereits" erhalten, obwohl ich ihn leicht ignorieren kann.

Ich benutze hauptsächlich mingw / msys, möchte aber etwas, das auch für andere Shells / Systeme funktioniert.

Ich habe es versucht, aber es hat nicht funktioniert, irgendwelche Ideen?

ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
KPexEA
quelle

Antworten:

106

Unter UNIX Verwenden Sie einfach Folgendes:

mkdir -p $(OBJDIR)

Die Option -p für mkdir verhindert die Fehlermeldung, wenn das Verzeichnis vorhanden ist.

tchen
quelle
39
"Halten Sie sich im Allgemeinen an die weit verbreiteten (normalerweise von Posix angegebenen) Optionen und Funktionen dieser Programme. Verwenden Sie beispielsweise nicht 'mkdir -p', so praktisch es auch sein mag, da einige Systeme dies nicht unterstützen überhaupt und mit anderen ist es nicht sicher für die parallele Ausführung. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel
8
-pfunktioniert nicht unter Windows, was vermutlich die Frage ist. Ich bin mir nicht sicher, wie dies jemals akzeptiert wurde.
Antimon
2
Stimmen Sie für Windows ab: connect.microsoft.com/PowerShell/feedback/details/1074131/…
Vulkanischer Rabe
1
@Antimony Sie haben wahrscheinlich etwas falsch gemacht, wenn Sie GNU Make außerhalb der MinGW-Shell verwenden. Ihre Wahl.
yyny
"Unter UNIX Verwenden Sie einfach diese ..." - Ist make -pUnix oder Linux?
JWW
122

Wenn Sie sich die offizielle Make-Dokumentation ansehen, ist hier ein guter Weg, dies zu tun:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    mkdir -p $(OBJDIR)

Sie sollten hier die Verwendung des | sehen Rohrbetreiber, der nur eine Auftragsvoraussetzung definiert. Dies bedeutet, dass das $(OBJDIR)Ziel vorhanden sein sollte (anstatt neuer ), um das aktuelle Ziel zu erstellen.

Beachten Sie, dass ich verwendet habe mkdir -p. Das -pFlag wurde im Vergleich zum Beispiel der Dokumente hinzugefügt. Weitere Antworten finden Sie in anderen Antworten.

ofavre
quelle
4
Dies ist definitiv der offizielle Weg zu diesem Problem.
lcltj
@CraigMcQueen: Ich meinte wirklich "das $(OBJDIR)Ziel sollte existieren " . Überprüfen Sie das Vorhandensein von Dateien (und Zeitstempel), um zu entscheiden, ob das Ziel erstellt werden muss. Daher hier, wenn $(OBJDIR)nicht vorhanden ist wird es sie bauen, so dass es existiert , um das aktuelle Ziel zu bauen $(OBJS), die innerhalb erstellt werden.
ofavre
Ich glaube, ich habe buchstäblich jede andere Kombination versucht, um dieses Problem zu lösen, bevor ich es gefunden habe (ich versuche, Makefiles zu generieren, die so allgemein wie möglich zwischen Windows / Linux sind) - dies ist so ziemlich die einzige, die ich zum Laufen bringen könnte, aber es funktioniert so gut! :)
code_fodder
67

Sie können den Testbefehl verwenden:

test -d $(OBJDIR) || mkdir $(OBJDIR)
Skymt
quelle
7
Dies ist die bevorzugte Methode, wenn Ihre Makefiles sowohl unter Windows (mit gnuwin32 und PowerShell) als auch unter POSIX-kompatiblen Betriebssystemen funktionieren sollen.
Kris Hardy
6
Vorausgesetzt, Sie haben das Paket gnuwin32 CoreUtils, um das Dienstprogramm 'test' bereitzustellen.
Davenpcj
2
Viel zu spät hier, aber ich möchte darauf hinweisen, dass diese Lösung beim parallelen Erstellen ( make -j) aufgrund einer Race-Bedingung zwischen dem Testen des Verzeichnisses auf Existenz und dem Erstellen des Verzeichnisses beschädigt werden kann. Ein Job kann testen, ob er nicht vorhanden ist, aber bevor er erstellt wird, erstellt ein anderer Job das Verzeichnis. Wenn der erste versucht, es zu schaffen, schlägt dies fehl, da das Verzeichnis bereits vorhanden ist. Die beste Lösung in diesem Fall besteht darin, nur die in der Antwort von @ TeKa genannten Bestellvoraussetzungen zu verwenden (dies sollte die akzeptierte Antwort sein).
C0deH4cker
@MarcusJ Funktioniert unter meinem OS X El Capitan. Welche Version hat es kaputt gemacht?
Franklin Yu
@ C0deH4cker - Vergib mir meine Unwissenheit ... Welche Antwort ist TeKa? Ich denke, TeKa hat seinen Namen geändert, seit Sie den Kommentar abgegeben haben.
JWW
18

Hier ist ein Trick, den ich mit GNU make zum Erstellen von Compiler-Ausgabeverzeichnissen verwende. Definieren Sie zuerst diese Regel:

  %/.d:
          mkdir -p $(@D)
          touch $@

Machen Sie dann alle Dateien, die in das Verzeichnis verschoben werden, von der D-Datei in diesem Verzeichnis abhängig:

 obj/%.o: %.c obj/.d
    $(CC) $(CFLAGS) -c -o $@ $<

Beachten Sie die Verwendung von $ <anstelle von $ ^.

Verhindern Sie schließlich, dass die .d-Dateien automatisch entfernt werden:

 .PRECIOUS: %/.d

Das Überspringen der D-Datei und abhängig vom Verzeichnis funktioniert nicht, da die Verzeichnisänderungszeit jedes Mal aktualisiert wird, wenn eine Datei in dieses Verzeichnis geschrieben wird, was bei jedem Aufruf von make eine Neuerstellung erzwingen würde.

Lars Hamrén
quelle
1
Ah, danke, ich habe versucht, so etwas zum Laufen zu bringen. Ich möchte nicht "test -d foo || mkdir foo" für mehrere Ziele, da die Verwendung von "make -j2" sie gleichzeitig testet, wobei beide Tests false ergeben und dann zwei Prozesse versuchen, mkdir, the letzte von ihnen scheitern.
Unhammer
13

Wenn es für Sie kein Problem ist, das Verzeichnis bereits zu haben, können Sie stderr einfach für diesen Befehl umleiten und die Fehlermeldung entfernen:

-mkdir $(OBJDIR) 2>/dev/null
andrewdotnich
quelle
Dies hat den Vorteil, auch unter Windows zu arbeiten. Vergessen Sie nicht das '-' vor dem Befehl, damit make nicht auf Kaution geht.
Michael Burr
11

In Ihrem Makefile:

target:
    if test -d dir; then echo "hello world!"; else mkdir dir; fi
Lee H.
quelle
10

Unter Windows

if not exist "$(OBJDIR)" mkdir $(OBJDIR)

Unter Unix | Linux

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
wmad
quelle
Dieser für Windows.
Prüfsumme
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
Wotanii
Die erste Version ist für Windows, die zweite Option funktioniert unter Linux, wie @checksum sagte
Edgard Leal
Die erste Windows-Version ist nicht atomar und funktioniert daher nicht, wenn ein anderer Prozess (z. B. eine Regel im Parallelmodus) das Verzeichnis zwischen "nicht vorhanden" und "mkdir" erstellt.
Petr Vepřek
7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif
Michael McCarty
quelle
Dies funktioniert gut für Mingws Marken, ohne dass zusätzliche Werkzeuge erforderlich sind.
Davenpcj
6
$(OBJDIR):
    mkdir $@

Das funktioniert auch für mehrere Verzeichnisse, z.

OBJDIRS := $(sort $(dir $(OBJECTS)))

$(OBJDIRS):
    mkdir $@

Das Hinzufügen $(OBJDIR)als erstes Ziel funktioniert gut.

Martin Fido
quelle
3

Es funktioniert unter mingw32 / msys / cygwin / linux

ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif
lygstate
quelle
0

Wenn Sie den Rückkehrcode explizit ignorieren und den Fehlerstrom ausgeben, ignoriert Ihr make den Fehler, wenn er auftritt:

mkdir 2>/dev/null || true

Dies sollte keine Renngefahr in einer parallelen Marke verursachen - aber ich habe es nicht getestet, um sicherzugehen.

Andrew
quelle
0

Ein bisschen einfacher als Lars 'Antwort:

something_needs_directory_xxx : xxx/..

und generische Regel:

%/.. : ;@mkdir -p $(@D)

Keine Touch-Dateien zum Aufräumen oder Erstellen .PRECIOUS :-)

Wenn Sie einen weiteren kleinen generischen Gmake-Trick sehen möchten oder wenn Sie an nicht rekursivem Make mit minimalem Gerüst interessiert sind, sollten Sie sich zwei weitere billige Gmake-Tricks und die anderen Make-bezogenen Beiträge in diesem Blog ansehen.

Mischa
quelle