come prevenire "la directory esiste già errore" in un makefile quando si usa mkdir


109

Ho bisogno di generare una directory nel mio makefile e vorrei non ricevere ripetutamente l'errore "directory già esistente" anche se posso ignorarlo facilmente.

Uso principalmente mingw / msys ma vorrei qualcosa che funzioni anche su altre shell / sistemi.

Ho provato questo ma non ha funzionato, qualche idea?

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

Risposte:


106

Su UNIX basta usare questo:

mkdir -p $(OBJDIR)

L'opzione -p di mkdir impedisce il messaggio di errore se la directory esiste.


39
"In generale, attenersi alle opzioni e alle caratteristiche ampiamente supportate (di solito specificate da posix) di questi programmi. Ad esempio, non usare 'mkdir -p', per quanto conveniente possa essere, perché alcuni sistemi non lo supportano affatto e con altri, non è sicuro per l'esecuzione parallela. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel

8
-pnon funziona su Windows, che è presumibilmente ciò su cui si pone la domanda. Non sono sicuro di come sia mai stato accettato.
Antimonio


1
@Antimony Probabilmente hai fatto qualcosa di sbagliato se stai usando GNU Make al di fuori della shell MinGW. La tua scelta, però.
yyny

"Su UNIX usa questo ..." - make -pUnix o Linux?
jww

122

Guardando la documentazione ufficiale di make , ecco un buon modo per farlo:

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)

Dovresti vedere qui l'utilizzo del | operatore pipe, definendo un unico prerequisito dell'ordine. Significa che l' $(OBJDIR)obiettivo dovrebbe essere esistente (invece che più recente ) per costruire l'obiettivo corrente.

Nota che ho usato mkdir -p. Il -pflag è stato aggiunto rispetto all'esempio dei documenti. Vedi altre risposte per un'altra alternativa.


4
Questo è sicuramente il modo ufficiale per risolvere questo problema.
lcltj

@ CraigMcQueen: volevo davvero dire "l' $(OBJDIR)obiettivo dovrebbe essere esistente " . Effettua controlli per la presenza di file (e timestamp) per decidere se è necessario creare l'obiettivo. Quindi qui, se $(OBJDIR)è assente, lo costruirà, in modo che sia esistente per costruire l'obiettivo corrente $(OBJS), che verrà creato all'interno.
ofavre

Penso di aver letteralmente provato ogni altra combinazione per risolvere questo problema prima di trovarlo (sto cercando di generare makefile il più generici possibile tra windows / linux) - questo è praticamente l'unico che potrei far funzionare, ma è funziona così bene! :)
code_fodder

67

Puoi usare il comando test:

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

7
Questo è il modo preferito se hai bisogno che i tuoi makefile funzionino sia in Windows (con gnuwin32 e PowerShell) che in sistemi operativi conformi a POSIX.
Kris Hardy

6
FORNITO di avere il pacchetto gnuwin32 CoreUtils per fornire l'utilità 'test'.
davenpcj

2
Molto tardi qui, ma vorrei sottolineare che questa soluzione può non funzionare quando make -jsi crea in parallel ( ) a causa di una condizione di competizione tra il momento in cui la directory viene testata per l'esistenza e viene creata. Un lavoro può verificare che non sia presente, ma prima di crearlo, un altro lavoro crea la directory. Quindi, quando il primo tenta di farlo, fallirà perché la directory esiste già. La soluzione migliore in questo caso è usare i prerequisiti di solo ordine come menzionato nella risposta di @ TeKa (che dovrebbe essere la risposta accettata).
C0deH4cker

@ MarcusJ Funziona sul mio OS X El Capitan. Quale versione l'ha rotto?
Franklin Yu

@ C0deH4cker - Perdona la mia ignoranza ... Quale risposta è TeKa? Penso che TeKa abbia cambiato il suo nome da quando hai fatto il commento.
jww

18

Ecco un trucco che uso con GNU per creare directory di output del compilatore. Per prima cosa definisci questa regola:

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

Quindi rendi tutti i file che entrano nella directory dipendenti dal file .d in quella directory:

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

Nota l'uso di $ <invece di $ ^.

Infine, evita che i file .d vengano rimossi automaticamente:

 .PRECIOUS: %/.d

Saltare il file .d e dipendere direttamente dalla directory non funzionerà, poiché l'ora di modifica della directory viene aggiornata ogni volta che un file viene scritto in quella directory, il che forzerebbe la ricostruzione ad ogni invocazione di make.


1
Ah, grazie, stavo cercando di far funzionare qualcosa del genere. Non voglio "test -d foo || mkdir foo" su diversi obiettivi, da allora l'uso di "make -j2" li testerà allo stesso tempo, entrambi i test danno false, e quindi due processi proveranno a mkdir, il l'ultimo di loro fallisce.
unhammer

13

Se avere la directory già esistente non è un problema per te, potresti semplicemente reindirizzare stderr per quel comando, eliminando il messaggio di errore:

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

Questo ha il vantaggio di lavorare anche su Windows. Non dimenticare il "-" davanti al comando, quindi make non salta.
Michael Burr

11

All'interno del tuo makefile:

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

10

Su Windows

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

Su Unix | Linux

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

Questo per Windows.
checksum

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

La prima versione è per Windows, la seconda opzione funziona su Linux come ha detto @checksum
Edgard Leal

La prima versione di Windows non è atomica quindi non funziona quando un altro processo (es. Una regola in modalità parallela) crea la directory tra "non esiste" e "mkdir".
Petr Vepřek

7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif

Questo funziona bene per la marca di mingw senza richiedere strumenti aggiuntivi.
davenpcj

6
$(OBJDIR):
    mkdir $@

Che funziona anche per più directory, ad es.

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

$(OBJDIRS):
    mkdir $@

L'aggiunta $(OBJDIR)come primo obiettivo funziona bene.


3

Funziona sotto mingw32 / msys / cygwin / linux

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

0

Se ignori esplicitamente il codice di ritorno e scarichi il flusso di errore, il tuo make ignorerà l'errore se si verifica:

mkdir 2>/dev/null || true

Questo non dovrebbe causare un pericolo di gara in una marca parallela, ma non l'ho testato per esserne sicuro.


0

Un po 'più semplice della risposta di Lars:

something_needs_directory_xxx : xxx/..

e regola generica:

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

Nessun file touch da pulire o creare. PREZIOSO :-)

Se vuoi vedere un altro piccolo trucco generico di gmake, o se sei interessato a make non ricorsivo con uno scaffolding minimo, potresti dare un'occhiata a altri due trucchi gmake economici e gli altri post relativi al make in quel blog.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.