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

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.


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


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.


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)

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.


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

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

10

Unter Windows

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

Unter Unix | Linux

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi

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

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.


3

Es funktioniert unter mingw32 / msys / cygwin / linux

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

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.


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.