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

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.


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


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.


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)

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.


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

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

10

No Windows

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

No Unix | Linux

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

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

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.


3

Funciona em mingw32 / msys / cygwin / linux

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

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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.