cómo evitar el "error de directorio ya existe" en un archivo MAKE cuando se usa mkdir


109

Necesito generar un directorio en mi archivo MAKE y me gustaría no obtener el "error de directorio ya existe" una y otra vez, aunque puedo ignorarlo fácilmente.

Utilizo principalmente mingw / msys, pero me gustaría algo que también funcione en otros shells / sistemas.

Intenté esto pero no funcionó, ¿alguna idea?

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

Respuestas:


106

En UNIX Solo usa esto:

mkdir -p $(OBJDIR)

La opción -p de mkdir evita el mensaje de error si el directorio existe.


39
"En general, apéguese a las opciones y características ampliamente admitidas (generalmente especificadas por posix) de estos programas. Por ejemplo, no use 'mkdir -p', por conveniente que sea, porque algunos sistemas no lo admiten en absoluto y con otros, no es seguro para la ejecución paralela ". gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel

8
-pno funciona en Windows, que es presumiblemente de lo que se trata la pregunta. No estoy seguro de cómo esto fue aceptado.
Antimony


1
@Antimony Probablemente hizo algo mal si está usando GNU Make fuera del shell MinGW. Sin embargo, es tu elección.
yyny

"En UNIX, simplemente use esto ..." - ¿Es make -pUnix o Linux?
jww

122

Mirando la documentación oficial de make , aquí hay una buena manera de hacerlo:

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)

Debería ver aquí el uso de | operador de tubería, definiendo un prerrequisito exclusivo de pedido. Lo que significa que el $(OBJDIR)objetivo debería existir (en lugar de ser más reciente ) para construir el objetivo actual.

Tenga en cuenta que usé mkdir -p. La -pbandera se agregó en comparación con el ejemplo de los documentos. Vea otras respuestas para otra alternativa.


4
Esta es definitivamente la forma oficial de resolver este problema.
lcltj

@CraigMcQueen: Yo realmente quería decir "el $(OBJDIR)objetivo debe ser inexistente " . Verifique la presencia de archivos (y marcas de tiempo) para decidir si es necesario construir el destino. Por lo tanto, aquí, si $(OBJDIR)está ausente, lo construirá, de modo que existe para construir el objetivo actual $(OBJS), que se creará dentro.
ofavre

Creo que he probado literalmente todas las demás combinaciones para resolver este problema antes de encontrar esto (estoy tratando de generar archivos MAKE que sean tan genéricos como pueda entre Windows / Linux); este es prácticamente el único que pude hacer funcionar, pero funciona tan bien! :)
code_fodder

67

Puede usar el comando de prueba:

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

7
Esta es la forma preferida si necesita que sus archivos MAKE funcionen tanto en Windows (con gnuwin32 y PowerShell) como en sistemas operativos compatibles con POSIX.
Kris Hardy

6
SIEMPRE tiene el paquete gnuwin32 CoreUtils para proporcionar la utilidad 'prueba'.
davenpcj

2
Muy tarde aquí, pero me gustaría señalar que esta solución puede romperse cuando se construye en paralelo ( make -j) debido a una condición de carrera entre el momento en que se prueba la existencia del directorio y se crea. Un trabajo puede probar que no está allí, pero antes de crearlo, otro trabajo crea el directorio. Luego, cuando el primero intente hacerlo, fallará porque el directorio ya existe. La mejor solución en este caso es usar solo los requisitos previos de orden como se menciona en la respuesta de @ TeKa (que debería ser la respuesta aceptada).
C0deH4cker

@MarcusJ Funciona en mi OS X El Capitan. ¿Qué versión lo rompió?
Franklin Yu

@ C0deH4cker - Perdona mi ignorancia ... ¿Cuál es la respuesta TeKa? Creo que TeKa cambió su nombre desde que hiciste el comentario.
jww

18

Aquí hay un truco que uso con GNU make para crear directorios de salida del compilador. Primero defina esta regla:

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

Luego, haga que todos los archivos que van al directorio dependan del archivo .d en ese directorio:

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

Tenga en cuenta el uso de $ <en lugar de $ ^.

Finalmente, evite que los archivos .d se eliminen automáticamente:

 .PRECIOUS: %/.d

Omitir el archivo .d, y dependiendo directamente del directorio, no funcionará, ya que la hora de modificación del directorio se actualiza cada vez que se escribe un archivo en ese directorio, lo que forzaría la reconstrucción en cada invocación de make.


1
Ah, gracias, estaba tratando de hacer que algo como esto funcionara. No quiero "test -d foo || mkdir foo" en varios objetivos, ya que luego usar "make -j2" los probará al mismo tiempo, ambas pruebas darán falso, y luego dos procesos intentarán mkdir, el último de ellos fallando.
unhammer

13

Si tener el directorio ya existente no es un problema para usted, simplemente puede redirigir stderr para ese comando, deshaciéndose del mensaje de error:

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

Esto también tiene la ventaja de funcionar en Windows. No olvide el '-' delante del comando para que make no se salga.
Michael Burr

11

Dentro de su archivo MAKE:

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

10

En Windows

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

En Unix | Linux

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

Éste para Windows.
suma de comprobación

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

La primera versión es para Windows, la segunda opción funciona en Linux como dijo @checksum
Edgard Leal

La primera versión, Windows, no es atómica, por lo que no funciona cuando otro proceso (por ejemplo, una regla en modo paralelo) crea el directorio entre "no existe" y "mkdir".
Petr Vepřek

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

Esto funciona bien para la marca mingw sin necesidad de herramientas adicionales.
davenpcj

6
$(OBJDIR):
    mkdir $@

Lo que también funciona para varios directorios, por ejemplo.

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

$(OBJDIRS):
    mkdir $@

Agregar $(OBJDIR)como primer objetivo funciona bien.


3

Funciona bajo mingw32 / msys / cygwin / linux

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

0

Si ignora explícitamente el código de retorno y descarga el flujo de error, su marca ignorará el error si ocurre:

mkdir 2>/dev/null || true

Esto no debería causar un peligro de carrera en una marca paralela, pero no lo he probado para estar seguro.


0

Un poco más simple que la respuesta de Lars:

something_needs_directory_xxx : xxx/..

y regla genérica:

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

No hay archivos táctiles para limpiar o hacer. PRECIOSO :-)

Si desea ver otro pequeño truco genérico de gmake, o si está interesado en la creación no recursiva con un mínimo de andamios, le recomendamos que consulte Dos trucos más baratos de gmake y las otras publicaciones relacionadas con la creación en ese blog.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.