jak uniknąć błędu „katalog już istnieje” w pliku makefile podczas korzystania z mkdir


109

Muszę wygenerować katalog w moim pliku makefile i nie chciałbym, aby komunikat „katalog już istnieje” w kółko, mimo że mogę go łatwo zignorować.

Używam głównie mingw / msys, ale chciałbym czegoś, co działa również w innych powłokach / systemach.

Próbowałem tego, ale nie zadziałało, jakieś pomysły?

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

Odpowiedzi:


106

W systemie UNIX Po prostu użyj tego:

mkdir -p $(OBJDIR)

Opcja -p mkdir zapobiega wyświetlaniu komunikatu o błędzie, jeśli katalog istnieje.


39
„Ogólnie rzecz biorąc, trzymaj się szeroko obsługiwanych (zwykle określonych przez posix) opcji i funkcji tych programów. Na przykład nie używaj 'mkdir -p', choć może to być wygodne, ponieważ kilka systemów go nie obsługuje w ogóle, az innymi, nie jest bezpieczne do wykonywania równoległego. ” gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel

8
-pnie działa w systemie Windows, o co prawdopodobnie chodzi w pytaniu. Nie jestem pewien, jak to kiedykolwiek zostało zaakceptowane.
Antymon

2
W przypadku systemu Windows głosowanie w górę: connect.microsoft.com/PowerShell/feedback/details/1074131/ ...
vulcan raven

1
@Antimony Prawdopodobnie zrobiłeś coś złego, jeśli używasz GNU Make poza powłoką MinGW. Twój wybór.
yyny,

„W systemie UNIX po prostu użyj tego ...” - Czy make -pUnix czy Linux?
jww

122

Patrząc na oficjalną dokumentację marki , oto dobry sposób, aby to zrobić:

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)

Powinieneś zobaczyć tutaj użycie | operator potoku, definiowanie tylko warunku wstępnego zamówienia. Oznacza to, że $(OBJDIR)cel powinien istnieć (zamiast być nowszy ), aby zbudować bieżący cel.

Zwróć uwagę, że użyłem mkdir -p. -pFlagę dodaną w stosunku do przykładu z dokumentów. Zobacz inne odpowiedzi, aby uzyskać inną alternatywę.


4
To jest zdecydowanie oficjalna droga do tego problemu.
lcltj

@CraigMcQueen: I naprawdę oznaczało „THE $(OBJDIR)cel powinien być wcale . Sprawdź obecność pliku (i sygnatury czasowe), aby zdecydować, czy musi on zbudować cel. Dlatego tutaj, jeśli $(OBJDIR)go nie ma, zbuduje go, aby istniał , aby zbudować obecny cel $(OBJS), który zostanie utworzony wewnątrz.
ofavre

Wydaje mi się, że próbowałem dosłownie każdej innej kombinacji rozwiązania tego problemu przed znalezieniem tego (próbuję wygenerować pliki makefile, które są tak ogólne, jak tylko potrafię między oknami / linuksem) - jest to prawie jedyna, którą mogłem uruchomić, ale to działa tak dobrze! :)
code_fodder

67

Możesz użyć polecenia test:

test -d $(OBJDIR) || mkdir $(OBJDIR)

7
Jest to preferowany sposób, jeśli chcesz, aby pliki makefile działały zarówno w systemie Windows (z gnuwin32 i PowerShell), jak i na systemach operacyjnych zgodnych z POSIX.
Kris Hardy,

6
DOSTARCZONO, że masz pakiet gnuwin32 CoreUtils, który udostępnia narzędzie „test”.
davenpcj

2
Dość późno w tym miejscu, ale chciałbym zwrócić uwagę, że to rozwiązanie może się zepsuć podczas budowania równoległego ( make -j) z powodu wyścigu między testowaniem istnienia katalogu a jego utworzeniem. Jedno zadanie może sprawdzić, czy go tam nie ma, ale zanim je utworzy, inne zadanie tworzy katalog. Wtedy, gdy pierwszy spróbuje to zrobić, zakończy się niepowodzeniem, ponieważ katalog już istnieje. Najlepszym rozwiązaniem w tym przypadku jest użycie tylko warunków wstępnych zamówienia, jak wspomniano w odpowiedzi @ TeKa (która powinna być odpowiedzią zaakceptowaną).
C0deH4cker,

@MarcusJ Działa na moim OS X El Capitan. Która wersja to zepsuła?
Franklin Yu

@ C0deH4cker - Wybacz moją ignorancję ... Która odpowiedź to TeKa? Myślę, że TeKa zmienił swoje imię od czasu, gdy napisałeś komentarz.
jww

18

Oto sztuczka, której używam w GNU make do tworzenia katalogów wyjściowych kompilatora. Najpierw zdefiniuj tę zasadę:

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

Następnie uzależnij wszystkie pliki, które trafiają do katalogu, od pliku .d w tym katalogu:

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

Zwróć uwagę na użycie $ <zamiast $ ^.

Wreszcie, zapobiegaj automatycznemu usuwaniu plików .d:

 .PRECIOUS: %/.d

Pominięcie pliku .d i bezpośrednie uzależnienie od katalogu nie zadziała, ponieważ czas modyfikacji katalogu jest aktualizowany za każdym razem, gdy plik jest zapisywany w tym katalogu, co wymusiłoby przebudowę przy każdym wywołaniu make.


1
Ach, dzięki, próbowałem sprawić, żeby coś takiego zadziałało. Nie chcę "test -d foo || mkdir foo" na kilku celach, ponieważ wtedy użycie "make -j2" przetestuje je w tym samym czasie, oba testy dadzą false, a następnie dwa procesy spróbują mkdir, ostatni z nich zawodzi.
unhammer

13

Jeśli posiadanie już istniejącego katalogu nie stanowi dla ciebie problemu, możesz po prostu przekierować stderr dla tego polecenia, pozbywając się komunikatu o błędzie:

-mkdir $(OBJDIR) 2>/dev/null

Ma to tę zaletę, że działa również w systemie Windows. Nie zapomnij o znaku „-” przed komendą, więc make nie wyskakuje.
Michael Burr,

11

Wewnątrz pliku makefile:

target:
    if test -d dir; then echo "hello world!"; else mkdir dir; fi

10

W systemie Windows

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

W systemie Unix | Linux

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

Ten dla systemu Windows.
suma kontrolna

/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii

Pierwsza wersja jest przeznaczona dla systemu Windows, druga opcja działa w systemie Linux, jak powiedział @checksum
Edgard Leal

Pierwsza wersja, Windows, nie jest atomowa, więc nie działa, gdy inny proces (np. Reguła w trybie równoległym) tworzy katalog pomiędzy „nie istnieje” a „mkdir”.
Petr Vepřek


6
$(OBJDIR):
    mkdir $@

Który działa również dla wielu katalogów, np.

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

$(OBJDIRS):
    mkdir $@

Dodanie $(OBJDIR)jako pierwszego celu działa dobrze.


3

Działa pod mingw32 / msys / cygwin / linux

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

0

Jeśli jawnie zignorujesz kod powrotu i zrzucisz strumień błędów, Twój program zignoruje błąd, jeśli wystąpi:

mkdir 2>/dev/null || true

Nie powinno to powodować zagrożenia wyścigowego w przypadku równoległej marki - ale nie testowałem tego dla pewności.


0

Trochę prostsza niż odpowiedź Larsa:

something_needs_directory_xxx : xxx/..

i ogólna zasada:

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

Brak plików dotykowych do czyszczenia lub tworzenia. PRECIOUS :-)

Jeśli chcesz zobaczyć kolejną małą ogólną sztuczkę gmake lub jeśli interesuje Cię nierekurencyjne make z minimalnym rusztowaniem, możesz zechcieć zapoznać się z dwoma innymi tanimi sztuczkami gmake i innymi postami związanymi z make na tym blogu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.