como evitar “erro de diretório já existe” em um makefile ao usar mkdir

109

Eu preciso gerar um diretório no meu makefile e gostaria de não receber o erro "diretório já existe" repetidamente, embora possa facilmente ignorá-lo.

Eu uso principalmente mingw / msys, mas gostaria de algo que funcionasse em outros shells / sistemas também.

Já tentei mas não deu certo, alguma ideia?

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

Respostas:

106

No UNIX Basta usar isto:

mkdir -p $(OBJDIR)

A opção -p para mkdir evita a mensagem de erro se o diretório existir.

tchen
fonte
39
"Geralmente, atenha-se às opções e recursos amplamente suportados (geralmente especificados por posix) desses programas. Por exemplo, não use 'mkdir -p', por mais conveniente que seja, porque alguns sistemas não o suportam de forma alguma e com outros, não é seguro para execução paralela. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel
8
-pnão funciona no Windows, que é provavelmente o motivo da pergunta. Não tenho certeza de como isso foi aceito.
Antimônio
2
Para Windows, vote a favor: connect.microsoft.com/PowerShell/feedback/details/1074131/…
vulcan raven
1
@Antimony Você provavelmente fez algo errado se estiver usando o GNU Make fora do shell do MinGW. Sua escolha, entretanto.
yyny
"No UNIX Basta usar isso ..." - É make -pUnix ou Linux?
jww
122

Olhando a documentação oficial do make , aqui está uma boa maneira de fazer isso:

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)

Você deve ver aqui o uso do | operador de tubulação, definindo um pré-requisito de pedido apenas. O que significa que o $(OBJDIR)alvo deve ser existente (em vez de mais recente ) para construir o alvo atual.

Observe que eu usei mkdir -p. O -psinalizador foi adicionado em comparação com o exemplo dos documentos. Veja outras respostas para outra alternativa.

Ofavre
fonte
4
Esta é definitivamente a forma oficial de resolver este problema.
lcltj
@CraigMcQueen: Eu realmente quis dizer "o $(OBJDIR)alvo deve existir " . Faça verificações de presença de arquivo (e carimbos de data / hora) para decidir se é necessário construir o destino. Portanto, aqui, se $(OBJDIR)estiver ausente, ele o construirá, de modo que exista para construir o alvo atual $(OBJS), que será criado dentro.
ofavre
Acho que tentei literalmente todas as outras combinações para resolver este problema antes de encontrar isso (estou tentando gerar makefiles que são tão genéricos quanto possível entre windows / linux) - este é praticamente o único que consegui fazer funcionar, mas funciona tão bem! :)
code_fodder
67

Você pode usar o comando de teste:

test -d $(OBJDIR) || mkdir $(OBJDIR)
skymt
fonte
7
Esta é a forma preferida se você precisa que seus makefiles funcionem tanto no Windows (com gnuwin32 e PowerShell) quanto em sistemas operacionais compatíveis com POSIX.
Kris Hardy
6
DESDE que você tenha o pacote gnuwin32 CoreUtils para fornecer o utilitário 'test'.
davenpcj
2
Bem tarde aqui, mas gostaria de salientar que esta solução pode falhar ao compilar em parallel ( make -j) devido a uma condição de corrida entre o momento em que o diretório é testado para existência e criado. Um trabalho pode testar se ele não está lá, mas antes de criá-lo, outro trabalho cria o diretório. Então, quando o primeiro tentar fazer isso, ele irá falhar porque o diretório já existe. A melhor solução neste caso é usar os pré-requisitos de pedido somente conforme mencionado na resposta de @TeKa (que deve ser a resposta aceita).
C0deH4cker
@MarcusJ Funciona no meu OS X El Capitan. Qual versão o quebrou?
Franklin Yu
@ C0deH4cker - Perdoe minha ignorância ... Qual resposta é TeKa? Acho que TeKa mudou de nome desde que você fez o comentário.
jww
18

Aqui está um truque que uso com o GNU make para criar diretórios de saída do compilador. Primeiro defina esta regra:

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

Em seguida, torne todos os arquivos que vão para o diretório dependentes do arquivo .d nesse diretório:

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

Observe o uso de $ <em vez de $ ^.

Por fim, evite que os arquivos .d sejam removidos automaticamente:

 .PRECIOUS: %/.d

Ignorar o arquivo .d e, dependendo diretamente do diretório, não funcionará, pois a hora de modificação do diretório é atualizada toda vez que um arquivo é gravado naquele diretório, o que forçaria a reconstrução a cada invocação de make.

Lars Hamrén
fonte
1
Ah, obrigado, eu estava tentando fazer algo assim funcionar. Eu não quero "test -d foo || mkdir foo" em vários objetivos, pois usar "make -j2" irá testá-los ao mesmo tempo, ambos os testes dando falso, e então dois processos tentarão mkdir, o último deles falhando.
unhammer
13

Se o diretório já existente não for um problema para você, basta redirecionar stderr para esse comando, eliminando a mensagem de erro:

-mkdir $(OBJDIR) 2>/dev/null
andrewdotnich
fonte
Isso tem a vantagem de funcionar no Windows também. Não se esqueça do '-' na frente do comando para que o make não desista.
Michael Burr
11

Dentro do seu makefile:

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

No Windows

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

No Unix | Linux

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
wmad
fonte
Este é para Windows.
checksum
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii
A primeira versão é para Windows, a segunda opção funciona no Linux como @checksum disse
Edgard Leal
A primeira versão, Windows, não é atômica, então não funciona quando outro processo (por exemplo, uma regra em modo paralelo) cria o diretório entre "não existe" e "mkdir".
Petr Vepřek
7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif
Michael McCarty
fonte
Isso funciona bem para o make do Mingw sem a necessidade de ferramentas extras.
davenpcj
6
$(OBJDIR):
    mkdir $@

Que também funciona para vários diretórios, por exemplo.

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

$(OBJDIRS):
    mkdir $@

Adicionar $(OBJDIR)como primeiro alvo funciona bem.

Martin Fido
fonte
3

Funciona em mingw32 / msys / cygwin / linux

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

Se você ignorar explicitamente o código de retorno e descartar o fluxo de erro, o make irá ignorar o erro se ele ocorrer:

mkdir 2>/dev/null || true

Isso não deve causar um risco de corrida em uma marca paralela - mas não testei para ter certeza.

Andrew
fonte
0

Um pouco mais simples do que a resposta de Lars:

something_needs_directory_xxx : xxx/..

e regra genérica:

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

Nenhum arquivo de toque para limpar ou fazer .PRECIOUS :-)

Se você quiser ver outro pequeno truque genérico do gmake, ou se estiver interessado no make não recursivo com o mínimo de estrutura, verifique mais dois truques gmake baratos e as outras postagens relacionadas ao make naquele blog.

Mischa
fonte