123 votes

comment éviter l'erreur "directory already exists error" dans un makefile lors de l'utilisation de mkdir

J'ai besoin de générer un répertoire dans mon fichier makefile et j'aimerais ne pas avoir l'erreur "directory already exists error" encore et encore même si je peux facilement l'ignorer.

J'utilise principalement mingw/msys mais j'aimerais quelque chose qui fonctionne aussi avec d'autres shells/systèmes.

J'ai essayé mais cela n'a pas fonctionné, une idée ?

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

129voto

TeKa Points 1368

Regarder la documentation officielle de make Voici une bonne façon de procéder :

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)

Vous devriez voir ici l'utilisation de l'opérateur de tuyauterie |, définissant une condition préalable de commande uniquement. Cela signifie que l'opérateur $(OBJDIR) devrait exister (au lieu de plus récent ) afin de construire la cible actuelle.

Notez que j'ai utilisé mkdir -p . Les -p a été ajouté par rapport à l'exemple de la documentation. Voir les autres réponses pour une autre alternative.

4 votes

C'est certainement la manière officielle de résoudre ce problème.

1 votes

@CraigMcQueen : Je voulais vraiment dire "le $(OBJDIR) L'objectif devrait être existant " . Make vérifie la présence de fichiers (et les horodatages) pour décider s'il doit construire la cible. Ainsi, ici, si $(OBJDIR) est absent, il le construira, de sorte qu'il soit existant afin de construire la cible actuelle $(OBJS) qui sera créé à l'intérieur.

0 votes

Je pense que j'ai littéralement essayé toutes les autres combinaisons pour résoudre ce problème avant de trouver celle-ci (j'essaie de générer des makefiles qui sont aussi génériques que possible entre Windows/linux) - c'est à peu près la seule que j'ai pu faire fonctionner, et elle fonctionne si bien ! : )

127voto

tchen Points 1344

Sous UNIX, utilisez simplement ceci :

mkdir -p $(OBJDIR)

L'option -p de mkdir permet d'éviter le message d'erreur si le répertoire existe.

39 votes

"En règle générale, il convient de s'en tenir aux options et fonctionnalités largement supportées (généralement spécifiées par Posix) de ces programmes. Par exemple, n'utilisez pas 'mkdir -p', aussi pratique soit-il, car certains systèmes ne le supportent pas du tout et, pour d'autres, il n'est pas sûr pour l'exécution parallèle. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html

8 votes

-p ne fonctionne pas sous Windows, ce qui est vraisemblablement l'objet de la question. Je ne sais pas comment cette question a pu être acceptée.

2 votes

Pour Windows, votez en faveur de cette solution : connect.microsoft.com/PowerShell/feedback/details/1074131/

71voto

skymt Points 2156

Vous pouvez utiliser la commande test :

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

8 votes

C'est la méthode préférée si vous avez besoin que vos makefiles fonctionnent à la fois sous Windows (avec gnuwin32 et PowerShell) et sous les systèmes d'exploitation compatibles avec POSIX.

8 votes

A CONDITION que vous ayez le paquetage CoreUtils de gnuwin32 pour fournir l'utilitaire 'test'.

2 votes

Je suis en retard, mais j'aimerais souligner que cette solution peut se révéler inefficace lorsque l'on construit en parallèle ( make -j ) en raison d'une condition de concurrence entre le moment où l'existence du répertoire est testée et celui où il est créé. Un travail peut tester qu'il n'existe pas, mais avant de le créer, un autre travail crée le répertoire. Ensuite, lorsque le premier essaie de le créer, il échoue parce que le répertoire existe déjà. La meilleure solution dans ce cas est d'utiliser des prérequis de type order only comme mentionné dans la réponse de @TeKa (qui devrait être la réponse acceptée).

19voto

Lars Hamrén Points 303

Voici une astuce que j'utilise avec GNU make pour créer des répertoires de sortie de compilateur. Définissez d'abord cette règle :

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

Ensuite, tous les fichiers qui se trouvent dans le répertoire dépendent du fichier .d de ce répertoire :

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

Notez l'utilisation de $< au lieu de $^.

Enfin, empêcher la suppression automatique des fichiers .d :

 .PRECIOUS: %/.d

Sauter le fichier .d, et dépendre directement du répertoire, ne fonctionnera pas, car le temps de modification du répertoire est mis à jour chaque fois qu'un fichier est écrit dans ce répertoire, ce qui obligerait à le reconstruire à chaque invocation de make.

1 votes

Ah, merci, j'essayais de faire fonctionner quelque chose comme ça. Je ne veux pas de "test -d foo || mkdir foo" sur plusieurs objectifs, car alors l'utilisation de "make -j2" les testera en même temps, les deux tests donnant faux, et alors deux processus essaieront de mkdir, le dernier d'entre eux échouant.

11voto

Lee Points 1771

Dans votre fichier makefile :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X