comment éviter «l'erreur de répertoire existe déjà» dans un makefile lors de l'utilisation de mkdir

109

J'ai besoin de générer un répertoire dans mon makefile et je voudrais ne pas obtenir le "répertoire existe déjà une erreur" encore et encore, même si je peux facilement l'ignorer.

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

J'ai essayé ça mais ça n'a pas fonctionné, des idées?

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

Réponses:

106

Sous UNIX, utilisez simplement ceci:

mkdir -p $(OBJDIR)

L'option -p de mkdir empêche le message d'erreur si le répertoire existe.

tchen
la source
39
"En règle générale, tenez-vous en aux options et fonctionnalités largement prises en charge (généralement spécifiées par Posix) de ces programmes. Par exemple, n'utilisez pas 'mkdir -p', aussi pratique soit-il, car quelques systèmes ne le prennent pas en charge du tout et avec d'autres, il n'est pas sûr pour une exécution parallèle. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel
8
-pne fonctionne pas sous Windows, ce qui est vraisemblablement l'objet de la question. Je ne sais pas comment cela a été accepté.
Antimoine
2
Pour Windows, votez pour ceci: connect.microsoft.com/PowerShell/feedback/details/1074131/…
vulcan raven
1
@Antimoine Vous avez probablement fait quelque chose de mal si vous utilisez GNU Make en dehors du shell MinGW. Votre choix, cependant.
yyny
"Sous UNIX, utilisez ceci ..." - Est-ce make -pqu'Unix ou Linux?
jww
122

En regardant la documentation officielle de make , voici une bonne façon de le faire:

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 du | opérateur de canalisation, définissant un prérequis d'ordre uniquement. Cela signifie que la $(OBJDIR)cible doit être existante (au lieu d'être plus récente ) afin de construire la cible actuelle.

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

ofavre
la source
4
C'est certainement la manière officielle de résoudre ce problème.
lcltj
@CraigMcQueen: Je voulais dire vraiment « l' $(OBJDIR)objectif doit être existant » . Vérifiez la présence de fichiers (et les horodatages) pour décider s'il a besoin de construire la cible. Par conséquent ici, si $(OBJDIR)est absent il va construire, de sorte qu'il est existant afin de construire la cible actuelle $(OBJS), qui sera créé à l' intérieur.
ofavre
Je pense que j'ai littéralement essayé toutes les autres combinaisons de résolution de ce problème avant de trouver cela (j'essaie de générer des makefiles aussi génériques que possible entre windows / linux) - c'est à peu près le seul que je pourrais faire fonctionner, mais il fonctionne si bien! :)
code_fodder
67

Vous pouvez utiliser la commande de test:

test -d $(OBJDIR) || mkdir $(OBJDIR)
skymt
la source
7
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 les systèmes d'exploitation compatibles POSIX.
Kris Hardy
6
A condition que vous ayez le package gnuwin32 CoreUtils pour fournir l'utilitaire «test».
davenpcj
2
Bien tard ici, mais j'aimerais souligner que cette solution peut être interrompue lors de la construction en parallèle ( make -j) en raison d'une condition de concurrence entre le moment où le répertoire est testé pour l'existence et sa création. Un travail peut tester qu'il n'est pas là, mais avant de le créer, un autre travail crée le répertoire. Ensuite, lorsque le premier essaie de le faire, il échouera car le répertoire existe déjà. La meilleure solution dans ce cas est de n'utiliser que les prérequis de commande comme mentionné dans la réponse de @ TeKa (qui devrait être la réponse acceptée).
C0deH4cker
@MarcusJ Fonctionne sur mon OS X El Capitan. Quelle version l'a cassé?
Franklin Yu
@ C0deH4cker - Pardonne mon ignorance ... Quelle réponse est TeKa? Je pense que TeKa a changé son nom depuis que vous avez fait le commentaire.
jww
18

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, rendez tous les fichiers qui vont dans le répertoire dépendant du fichier .d dans ce répertoire:

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

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

Enfin, empêchez les fichiers .d d'être supprimés automatiquement:

 .PRECIOUS: %/.d

Ignorer le fichier .d, et dépendre directement du répertoire, ne fonctionnera pas, car l'heure de modification du répertoire est mise à jour chaque fois qu'un fichier est écrit dans ce répertoire, ce qui forcerait la reconstruction à chaque appel de make.

Lars Hamrén
la source
1
Ah, merci, j'essayais de faire fonctionner quelque chose comme ça. Je ne veux pas de "test -d foo || mkdir foo" sur plusieurs buts, car utiliser "make -j2" les testera en même temps, les deux tests donnant faux, puis deux processus essaieront de mkdir, le le dernier échoue.
unhammer
13

Si le fait que le répertoire existe déjà n'est pas un problème pour vous, vous pouvez simplement rediriger stderr pour cette commande, en supprimant le message d'erreur:

-mkdir $(OBJDIR) 2>/dev/null
andrewdotnich
la source
Cela présente également l'avantage de fonctionner sous Windows. N'oubliez pas le «-» devant la commande, alors ne mettez pas la caution.
Michael Burr
11

Dans votre makefile:

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

Sous Windows

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

Sur Unix | Linux

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
wmad
la source
Celui-ci pour Windows.
somme de contrôle du
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii
La première version est pour Windows, la deuxième option fonctionne sur Linux comme l'a dit @checksum
Edgard Leal
La première version, Windows, n'est pas atomique donc elle ne fonctionne pas lorsqu'un autre processus (par exemple une règle en mode parallèle) crée le répertoire entre "not exist" et "mkdir".
Petr Vepřek
7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif
Michael McCarty
la source
Cela fonctionne bien pour la marque mingw sans nécessiter d'outils supplémentaires.
davenpcj
6
$(OBJDIR):
    mkdir $@

Ce qui fonctionne également pour plusieurs répertoires, par exemple.

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

$(OBJDIRS):
    mkdir $@

L'ajout $(OBJDIR)comme première cible fonctionne bien.

Martin Fido
la source
3

Cela fonctionne sous mingw32 / msys / cygwin / linux

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

Si vous ignorez explicitement le code de retour et videz le flux d'erreur, votre make ignorera l'erreur si elle se produit:

mkdir 2>/dev/null || true

Cela ne devrait pas causer de danger de course dans une marque parallèle - mais je ne l'ai pas testé pour être sûr.

Andrew
la source
0

Un peu plus simple que la réponse de Lars:

something_needs_directory_xxx : xxx/..

et règle générique:

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

Aucun fichier tactile à nettoyer ou à créer .PRECIOUS :-)

Si vous voulez voir une autre petite astuce générique de gmake, ou si vous êtes intéressé par une création non récursive avec un échafaudage minimal, vous voudrez peut-être consulter Deux autres astuces gmake bon marché et les autres articles liés à la fabrication dans ce blog.

Mischa
la source