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

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.


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


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.


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)

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.


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

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

10

Sous Windows

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

Sur Unix | Linux

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

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

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.


3

Cela fonctionne sous mingw32 / msys / cygwin / linux

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

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.


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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.