Makefile (Génération De Dépendance Automatique)

Juste pour la terminologie rapide:

#basic makefile rule
target: dependencies
    recipe

Le problème: je veux générer les dépendances automatiquement.

Par exemple, j'espère transformer ceci:

#one of my targets
file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)

Dans ceci:

#one of my targets
file.o: $(GENERATE)
    $(COMPILE)

Et je ne suis pas trop sûr si c'est possible..

Ce que je sais:

Je peux utiliser ce drapeau du compilateur:

g++ -MM file.cpp

Et il retournera la cible et la dépendance appropriées.
donc, à partir de l'exemple, il retournerait:

file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h  

Cependant, 'make' ne permet pas moi pour écrire explicitement du code shell dans la section cible ou dépendance d'une règle: (
Je sais qu'il est un "faire" fonction appelée shell

Mais je ne peux pas tout à fait brancher cela en tant que dépendance et faire de la magie d'analyse car elle repose sur la macro $@ qui représente la cible.. ou du moins je pense que c'est ce que le problème est

J'ai même essayé de simplement remplacer le fichier".dépendance " cpp " avec cette fonction makefile et cela ne fonctionnera pas non plus..

#it's suppose to turn the $@ (file.o) into file.cpp
THE_CPP := $(addsuffix $(.cpp),$(basename $@))

#one of my targets
file.o: $(THE_CPP) 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
#this does not work

Donc partout google, il semble y avoir deux solutions. les deux que je ne comprends pas complètement.
De GNU Make Manuel

Un Site qui dit que le manuel GNU Make est obsolète

Donc, ma question ultime est: est-il possible de le faire comme je veux le faire,
et sinon, quelqu'un peut-il décomposer le code de l'un de ces sites et m'expliquer en détail comment ils fonctionnent. Je vais l'implémenter de l'une de ces façons si je dois le faire, mais je suis fatigué de simplement coller un morceau de code dans mon makefile avant de le comprendre

24
demandé sur stimulate 2011-11-06 11:28:29

7 réponses

Les nouvelles versions de GCC ont une option-MP qui peut être utilisée avec-MD. J'ai simplement ajouté -MP et-MD à la variable CPPFLAGS pour mon projet (je n'ai pas écrit de recette personnalisée pour compiler C++) et ajouté un " - include $(SRC:.rpc=.d)" en ligne.

L'utilisation de-MD et-MP donne un fichier de dépendance qui inclut à la fois les dépendances (sans avoir à utiliser de sed bizarre) et les cibles fictives (de sorte que la suppression des fichiers d'en-tête ne causera pas d'erreurs).

24
répondu teambob 2012-04-18 03:52:56

Pour manipuler les noms de fichiers lorsque vous connaissez déjà les dépendances, vous pouvez utiliser une règle de pattern:

file.o: %.o : %.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)

Et vous pouvez réutiliser la règle pour d'autres cibles:

# Note these two rules without recipes:
file.o: 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
anotherFile.o: 4.h 9.h yetAnother.h

file.o anotherFile.o: %.o : %.cpp
    $(COMPILE)

Mais si vous voulez faire pour comprendre la liste des dépendances automatiquement, la meilleure façon (que je connaisse) est génération avancée D'auto-dépendance . Il ressemble à ceci:

%.o : %.cc
        @g++ -MD -c -o $@ $<
        @cp $*.d $*.P; \
             sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                 -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
             rm -f $*.d

-include *.P

Fondamentalement, quand il construit file.o, il construit aussi file.d. Ensuite, il court file.d à travers un ahurissant commande sed qui transforme la liste des dépendances en une règle sans recettes. La dernière ligne est une instruction à include de telles règles qui existent. La logique ici est subtile et ingénieuse: vous n'avez pas réellement besoin des dépendances la première fois que vous construisez foo.o, Car Make sait déjà que foo.o doit être construit, car il n'existe pas. La prochaine fois que vous exécuterez Make, il utilisera la liste de dépendances qu'il a créée la dernière fois. Si vous modifiez l'un des fichiers de sorte qu'il est en fait une nouvelle dépendance qui N'est pas dans la liste, Make reconstruira toujours foo.o car Vous avez changé un fichier qui était une dépendance . Essayez, ça marche vraiment!

20
répondu Beta 2011-11-06 14:05:34

Excellentes réponses mais dans ma construction, je mets le .fichiers obj dans un sous-répertoire basé sur le type de construction (ie: debug vs. release). Ainsi, par exemple, si je construis debug, je mets tous les fichiers objet dans un dossier build/debug. C'était une tâche hallucinante d'essayer d'obtenir la commande multiligne sed ci-dessus pour utiliser le dossier de destination correct, mais après quelques expériences, je suis tombé sur une solution qui fonctionne très bien pour ma construction. J'espère que ça aidera quelqu'un d'autre aussi.

Voici un extrait de code:

# List my sources
CPP_SOURCES := foo.cpp bar.cpp

# If I'm debugging, change my output location
ifeq (1,$(DEBUG))
  OBJ_DIR:=./obj/debug
  CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x
else
  CXXFLAGS+= -s -O2 
  OBJ_DIR:=./obj/release
endif

# destination path macro we'll use below
df = $(OBJ_DIR)/$(*F)

# create a list of auto dependencies
AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES))

# include by auto dependencies
-include $(AUTODEPS)

.... other rules

# and last but not least my generic compiler rule
$(OBJ_DIR)/%.o: %.cpp 
    @# Build the dependency file
    @$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d
    @# Compile the object file
    @echo " C++ : " $< " => " $@
    @$(CXX) -c $< $(CXXFLAGS) -o $@

Maintenant pour les détails: La première exécution de CXX dans ma règle de construction générique est la plus intéressante. Notez que je n'utilise aucune commande "sed". Les versions plus récentes de gcc font tout ce dont j'avais besoin (j'utilise gcc 4.7.2).

- MM construit la règle de dépendance principale, y compris les en-têtes de projet, mais pas les en-têtes système. Si je l'ai laissé comme ça, mon .le fichier obj N'aurait pas le chemin correct. J'utilise donc l'option-MT pour spécifier le chemin" réel " vers mon .obj destination. (à l'aide de la macro" DF " que j'ai créé).
J'utilise également une option second-MT pour m'assurer que le fichier de dépendance résultant (ie: .d file) a le chemin correct, et qu'il est inclus dans la liste cible et a donc les mêmes dépendances que le fichier source.

, Dernier mais non le moindre est l'inclusion de l'-MP option. Cela dit à gcc de créer également des règles tronquées pour chaque en-tête résolvant le problème qui se produit si je supprime un en-tête provoquant la génération d'une erreur.

Je soupçonne que puisque j'utilise gcc pour toute la génération de dépendances au lieu de passer à sed, ma construction est plus rapide (bien que je n'ai pas encore prouvé cela puisque ma construction est relativement petite à ce stade). Si vous voyez des façons dont je peux améliorer cela, je suis toujours ouvert aux suggestions. Profitez

7
répondu Zoccadoum 2015-02-10 13:31:39

Tout d'Abord, vous pouvez avoir THE_CPP=$(patsubst %.o,%.cpp,$@)

Ensuite, vous pouvez exécuter make -p pour comprendre les règles intégrées de make

Une façon habituelle de faire pourrait être de générer les dépendances makefile dans les fichiers *.md:

%.o: %.c
       $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,$@)

Et plus tard dans votre Makefile y compris les, avec quelque chose comme

-include $(wildcard *.md)

, Mais vous pouvez également envisager l'utilisation d'autres constructeurs comme omake et beaucoup d'autres

4
répondu Basile Starynkevitch 2011-11-06 07:44:38

Pour mémoire, voici comment je génère automatiquement des dépendances maintenant:

CPPFLAGS = -std=c++1y -MD -MP 

SRC = $(wildcard *.cpp)
all: main

main: $(SRC:%.cpp=%.o)
    g++ $(CPPFLAGS) -o $@ $^

-include $(SRC:%.cpp=%.d)

Les drapeaux du compilateur-MD et-MP aident à faire l'affaire.

4
répondu Trevor Hickey 2014-01-13 07:49:01

WOOO! J'ai réussi à obtenir le code dans le post de Beta pour travailler sur un petit projet de test.
Je devrais noter, pour quelqu'un d'autre qui peut rencontrer cela, Si vous utilisez le shell bash(que j'étais), vous devrez ajouter un caractère d'échappement devant le signe pound Pour éviter de faire du reste de l'expression un commentaire. (voir 4ème ligne de code)

%.o : %.cpp  
    g++ -c -MD -o $@ $<  
    cp $*.d $*.P; \  
    sed -e 's/\#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \  
        -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \  
    rm -f $*.d  
-include *.P  

Maintenant, je veux partager des informations que j'ai trouvées dans Gestion de projets avec GNU Make, 3ème édition. parce qu'il est souligne quelques questions importantes à ce sujet, et fournit du code que je ne comprends toujours pas encore complètement.
Une méthode apparaît dans le livre qui est similaire à la méthode trouvée sur la page de manuel Make .
Il ressemble à ceci:

include $(subst .c,.d,$(SOURCES))

%.d: %.c
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\).o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

C'est ce que je crois qui se passe.
Tout de suite, "faire" souhaite ".d " fichier pour chaque fichier source.
Parce que pas de .d fichiers initialement existent, le bloc de code est exécuté, encore et encore afin de créer tous les disparus .d fichiers.
Cela signifie faire va recommencer encore et encore jusqu'à ce que chaque .le fichier d est créé et inclus dans le makefile.
Chacun ".d " fichier est ce que bêta a dit: une cible avec un ensemble de dépendances et aucune recette.

Si un fichier d'en-tête est modifié, les règles incluses dans, devront d'abord mettre à jour les dépendances. C'est ce qui me jette un peu, comment est-ce que le morceau de code peut être appelé à nouveau? Il est utilisé pour mettre à jour .d fichiers, donc si un .h changements de fichier Comment s'appelle-t-il? Hormis ceci, je me rends compte que la règle par défaut est utilisée pour compiler l'objet. Toute clarification / idée fausse à cette explication est appréciée.


Plus Tard dans le livre, il signale des problèmes avec cette méthode, et des problèmes qui, je crois, existent également dans L'implémentation avancée de la génération de dépendance automatique.
Problème 1: {[25] }c'est inefficace. "faire" doit redémarrer à chaque fois qu'il fait un .d fichier
Problème 2: faire génère des messages d'avertissement pour tous les disparus .d fichiers - qui est la plupart du temps juste une nuisance et peut être caché en ajoutant un " - " devant l'instruction include.
problème 3: Si vous supprimez un fichier src parce qu'il n'est plus nécessaire, 'make' Plantera la prochaine fois que vous essayez de compiler car certains .le fichier d A Le src manquant en tant que dépendance, et comme il n'y a pas de règle pour recréer ce src, make refusera d'aller plus loin.

Ils disent qu'un correctif à ces problèmes est la méthode de Tromey, mais le code semble très différent du code sur le site web. Peut-être que c'est juste parce qu'ils ont utilisé des macros, en ont fait un appel de fonction, et l'ont écrit légèrement différent. Je suis toujours à la recherche, mais je voulais partager quelques découvertes que j'ai faites jusqu'à présent. Espérons que cela ouvre un peu plus de discussion, et me rapproche du fond de tout cela.

1
répondu Trevor Hickey 2013-10-08 18:13:16

, je préfère utiliser $(shell ...) fonction avec trouver. Voici un exemple d'un de mes Makefiles:

SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc

# Get Only the Internal Structure of Directories from SRCDIR
STRUCTURE := $(shell find $(SRCDIR) -type d)

#Filter-out hidden directories
STRUCTURE := $(filter-out $(shell find $(SRCDIR)/.* -type d),$(STRUCTURE))

# Get All Files From STRUCTURE
CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))


## Filter Only Specific Files
SRCFILES := $(filter %.c,$(CODEFILES))
HDRFILES := $(filter %.h,$(CODEFILES))
OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o))
DOCFILES := $(addprefix $(DOCDIR)/,             \
            $(addsuffix .md,                    \
            $(basename $(SRCFILES))))


# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))

Dans cette approche, j'obtiens d'abord toute la structure de répertoire interne, avec n'importe quelle profondeur. Ensuite, je reçois tous les fichiers à l'intérieur de la Structure. En ce moment, je peux utiliser filter, filter-out, addsuffix, etc, pour obtenir exactement ce dont j'ai besoin à chaque fois.

Cet exemple couvre *.fichiers c, mais vous pouvez le changer en *.rpc ainsi.

0
répondu 2015-09-29 14:01:53