Nous sommes tenus d'utiliser un Makefile pour tout rassembler pour notre projet, mais notre professeur ne nous a jamais montré comment.
Je n'ai que un fichier, a3driver.cpp
. Le conducteur importe une classe à partir d' un emplacement, "/user/cse232/Examples/example32.sequence.cpp"
.
C'est tout. Tout le reste est contenu dans le .cpp
.
Comment pourrais-je faire un Makefile simple qui crée un exécutable appelé a3a.exe
?
Réponses:
Comme c'est pour Unix, les exécutables n'ont pas d'extensions.
Une chose à noter est qu'il
root-config
s'agit d'un utilitaire qui fournit les bons indicateurs de compilation et de liaison; et les bonnes bibliothèques pour créer des applications contre root. Ce n'est qu'un détail lié à l'audience d'origine de ce document.Fais-moi bébé
ou vous n'oublierez jamais la première fois que vous vous êtes fait
Une discussion introductive sur make et comment écrire un simple makefile
Qu'est-ce que Make? Et pourquoi devrais-je m'en soucier?
L'outil appelé Make est un gestionnaire de dépendances de génération. Autrement dit, il prend soin de savoir quelles commandes doivent être exécutées dans quel ordre pour prendre votre projet logiciel à partir d'une collection de fichiers source, de fichiers objets, de bibliothèques, d'en-têtes, etc., etc. - dont certains peuvent avoir changé récemment --- et les transformer en une version correcte et à jour du programme.
En fait, vous pouvez également utiliser Make pour d'autres choses, mais je ne vais pas en parler.
Un Makefile trivial
Supposons que vous ayez un répertoire contenant:,
tool
tool.cc
tool.o
support.cc
support.hh
etsupport.o
qui dépendentroot
et sont censés être compilés dans un programme appelétool
, et supposez que vous avez piraté les fichiers source (ce qui signifie que l'existanttool
est désormais obsolète) et que vous souhaitez compiler le programme.Pour le faire vous-même, vous pourriez
Vérifiez si
support.cc
ousupport.hh
est plus récent quesupport.o
, et si c'est le cas, exécutez une commande commeVérifiez si l'un
support.hh
ou l' autretool.cc
est plus récent quetool.o
, et si c'est le cas, exécutez une commande commeVérifiez si
tool.o
est plus récent quetool
, et si c'est le cas, exécutez une commande commePhew! Quelle galère! Il y a beaucoup de choses à retenir et plusieurs chances de faire des erreurs. (BTW - les détails des lignes de commande présentées ici dépendent de notre environnement logiciel. Ceux-ci fonctionnent sur mon ordinateur.)
Bien sûr, vous pouvez simplement exécuter les trois commandes à chaque fois. Cela fonctionnerait, mais il ne s'adapte pas bien à un logiciel substantiel (comme DOGS qui prend plus de 15 minutes à compiler à partir de zéro sur mon MacBook).
Au lieu de cela, vous pouvez écrire un fichier appelé
makefile
comme ceci:et tapez simplement
make
sur la ligne de commande. Qui exécutera automatiquement les trois étapes ci-dessus.Les lignes non indentées ici ont la forme "cible: dépendances" et indiquent à Make que les commandes associées (lignes indentées) doivent être exécutées si l'une des dépendances est plus récente que la cible. Autrement dit, les lignes de dépendance décrivent la logique de ce qui doit être reconstruit pour s'adapter aux modifications dans divers fichiers. Si cela
support.cc
change, celasupport.o
doit être reconstruit, maistool.o
peut être laissé seul. Quand lessupport.o
modificationstool
doivent être reconstruites.Les commandes associées à chaque ligne de dépendance sont déclenchées par un onglet (voir ci-dessous) qui devrait modifier la cible (ou au moins la toucher pour mettre à jour l'heure de modification).
Variables, règles intégrées et autres avantages
À ce stade, notre makefile se souvient simplement du travail qui doit être fait, mais nous avons encore dû comprendre et taper chaque commande nécessaire dans son intégralité. Il ne doit pas en être ainsi: Make est un langage puissant avec des variables, des fonctions de manipulation de texte et toute une série de règles intégrées qui peuvent nous faciliter la tâche.
Créer des variables
La syntaxe pour accéder à une variable make est
$(VAR)
.La syntaxe d'affectation à une variable Make est:
VAR = A text value of some kind
(ouVAR := A different text value but ignore this for the moment
).Vous pouvez utiliser des variables dans des règles comme cette version améliorée de notre makefile:
ce qui est un peu plus lisible, mais nécessite encore beaucoup de frappe
Créer des fonctions
GNU make prend en charge une variété de fonctions pour accéder aux informations à partir du système de fichiers ou d'autres commandes du système. Dans ce cas, nous nous intéressons à
$(shell ...)
ce qui se développe à la sortie des arguments, et$(subst opat,npat,text)
qui remplace toutes les instances deopat
withnpat
dans le texte.Profiter de cela nous donne:
qui est plus facile à taper et beaucoup plus lisible.
Remarquerez que
Règles implicites et de modèle
Nous nous attendons généralement à ce que tous les fichiers source C ++ soient traités de la même manière, et Make fournit trois façons de le déclarer:
Des règles implicites sont intégrées, et quelques-unes seront discutées ci-dessous. Les règles de modèle sont spécifiées sous une forme comme
ce qui signifie que les fichiers objets sont générés à partir des fichiers source C en exécutant la commande indiquée, où la variable "automatique" se
$<
développe jusqu'au nom de la première dépendance.Règles intégrées
Make a toute une série de règles intégrées qui signifient que très souvent, un projet peut être compilé par un makefile très simple, en effet.
La règle GNU make intégrée pour les fichiers source C est celle présentée ci-dessus. De même, nous créons des fichiers objets à partir de fichiers source C ++ avec une règle comme
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.Les fichiers d'objet unique sont liés à l'aide
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, mais cela ne fonctionnera pas dans notre cas, car nous voulons lier plusieurs fichiers d'objet.Variables utilisées par les règles intégrées
Les règles intégrées utilisent un ensemble de variables standard qui vous permettent de spécifier les informations d'environnement local (comme où trouver les fichiers d'inclusion ROOT) sans réécrire toutes les règles. Les plus susceptibles de nous intéresser sont:
CC
- le compilateur C à utiliserCXX
- le compilateur C ++ à utiliserLD
- l'éditeur de liens à utiliserCFLAGS
- drapeau de compilation pour les fichiers source CCXXFLAGS
- drapeaux de compilation pour les fichiers source C ++CPPFLAGS
- drapeaux pour le préprocesseur c (incluent généralement les chemins de fichiers et les symboles définis sur la ligne de commande), utilisés par C et C ++LDFLAGS
- drapeaux de l'éditeur de liensLDLIBS
- bibliothèques à relierUn Makefile de base
En tirant parti des règles intégrées, nous pouvons simplifier notre makefile pour:
Nous avons également ajouté plusieurs cibles standard qui effectuent des actions spéciales (comme le nettoyage du répertoire source).
Notez que lorsque make est invoqué sans argument, il utilise la première cible trouvée dans le fichier (dans ce cas tous), mais vous pouvez également nommer la cible à obtenir, ce qui explique la
make clean
suppression des fichiers objet dans ce cas.Nous avons toujours toutes les dépendances codées en dur.
Quelques améliorations mystérieuses
Remarquerez que
make
alorsls -A
vous voyez un fichier nommé.depend
qui contient des choses qui ressemblent à faire des lignes de dépendanceAutre lecture
Connaître les bugs et les notes historiques
La langue d'entrée pour Make est sensible aux espaces. En particulier, les lignes d'action suivant les dépendances doivent commencer par un onglet . Mais une série d'espaces peut se ressembler (et en effet, il existe des éditeurs qui convertissent silencieusement les tabulations en espaces ou vice versa), ce qui se traduit par un fichier Make qui semble correct et ne fonctionne toujours pas. Cela a été identifié comme un bug au début, mais ( l'histoire raconte ), il n'a pas été corrigé, car il y avait déjà 10 utilisateurs.
(Ceci a été copié à partir d'un article wiki que j'ai écrit pour des étudiants diplômés en physique.)
la source
-pthread
flag permetgcc
de définir les macros nécessaires,-D_REENTRANT
n'est pas nécessaire.root-config
). Une alternative plus générale avec la même capacité devrait être proposée le cas échéant ou elle devrait simplement être laissée de côté. Je n'ai pas déçu en raison de la liste et de l'explication des macros de création les plus fréquemment utilisées.J'ai toujours pensé que c'était plus facile à apprendre avec un exemple détaillé, alors voici comment je pense aux makefiles. Pour chaque section, vous avez une ligne qui n'est pas en retrait et elle montre le nom de la section suivie des dépendances. Les dépendances peuvent être soit d'autres sections (qui seront exécutées avant la section actuelle) ou des fichiers (qui, s'ils sont mis à jour, entraîneront la réexécution de la section actuelle lors de la prochaine exécution
make
).Voici un exemple rapide (gardez à l'esprit que j'utilise 4 espaces où je devrais utiliser un onglet, Stack Overflow ne me permettra pas d'utiliser des onglets):
Lorsque vous tapez
make
, il choisira la première section (a3driver). a3driver dépend de a3driver.o, donc il ira à cette section. a3driver.o dépend de a3driver.cpp, il ne s'exécutera donc que si a3driver.cpp a changé depuis sa dernière exécution. En supposant qu'il a (ou n'a jamais été exécuté), il compilera a3driver.cpp dans un fichier .o, puis reviendra à a3driver et compilera l'exécutable final.Puisqu'il n'y a qu'un seul fichier, il pourrait même être réduit à:
La raison pour laquelle j'ai montré le premier exemple est qu'il montre la puissance des makefiles. Si vous devez compiler un autre fichier, vous pouvez simplement ajouter une autre section. Voici un exemple avec un secondFile.cpp (qui se charge dans un en-tête nommé secondFile.h):
De cette façon, si vous modifiez quelque chose dans secondFile.cpp ou secondFile.h et recompilez, il ne recompilera que secondFile.cpp (pas a3driver.cpp). Ou alternativement, si vous changez quelque chose dans a3driver.cpp, il ne recompilera pas secondFile.cpp.
Faites-moi savoir si vous avez des questions à ce sujet.
Il est également traditionnel d'inclure une section nommée "all" et une section nommée "clean". "all" construira généralement tous les exécutables, et "clean" supprimera les "build artifacts" comme les fichiers .o et les exécutables:
EDIT: Je n'ai pas remarqué que vous êtes sous Windows. Je pense que la seule différence est de changer le
-o a3driver
en-o a3driver.exe
.la source
Pourquoi tout le monde aime-t-il lister les fichiers source? Une simple commande find peut s'en occuper facilement.
Voici un exemple d'un Makefile C ++ simple. Il suffit de le déposer dans un répertoire contenant des
.C
fichiers puis de tapermake
...la source
Vous aviez deux options.
Option 1: makefile le plus simple = PAS DE MAKEFILE.
Renommez "a3driver.cpp" en "a3a.cpp", puis sur la ligne de commande, écrivez:
Et c'est tout. Si vous utilisez GNU Make, utilisez "make" ou "gmake" ou autre chose.
Option 2: un makefile à 2 lignes.
la source
nmake
. Lalink
ligne de commande semble également très spécifique à un compilateur particulier, et devrait à tout le moins documenter lequel.Votre fichier Make aura une ou deux règles de dépendance selon que vous compilez et liez avec une seule commande, ou avec une commande pour la compilation et une pour le lien.
La dépendance est un arbre de règles qui ressemble à ceci (notez que le retrait doit être un TAB):
Il doit y avoir une ligne vierge après les commandes pour une cible, et il ne doit pas y avoir de ligne vierge avant les commandes. La première cible dans le makefile est l'objectif global et les autres cibles ne sont construites que si la première cible en dépend.
Ainsi, votre makefile ressemblera à quelque chose comme ça.
la source
Je suggère (notez que le tiret est un TAB):
ou
Cette dernière suggestion est légèrement meilleure car elle réutilise les règles implicites de GNU Make. Cependant, pour fonctionner, un fichier source doit avoir le même nom que l'exécutable final (c'est-à-dire:
tool.c
ettool
).Remarquez qu'il n'est pas nécessaire de déclarer les sources. Les fichiers objets intermédiaires sont générés à l'aide d'une règle implicite. Par conséquent, cela
Makefile
fonctionne pour C et C ++ (et aussi pour Fortran, etc ...).Notez également, par défaut, que Makefile est utilisé
$(CC)
comme éditeur de liens.$(CC)
ne fonctionne pas pour la liaison de fichiers d'objets C ++. NousLINK.o
ne modifions que pour cela. Si vous souhaitez compiler du code C, vous n'avez pas à forcer laLINK.o
valeur.Bien sûr, vous pouvez également ajouter vos indicateurs de compilation avec variable
CFLAGS
et ajouter vos bibliothèquesLDLIBS
. Par exemple:Remarque: si vous devez utiliser des bibliothèques externes, je suggère d' utiliser pkg-config afin de définir correctement
CFLAGS
etLDLIBS
:Le lecteur attentif remarquera que cela
Makefile
ne se reconstruit pas correctement si un en-tête est modifié. Ajoutez ces lignes pour résoudre le problème:-MMD
permet de construire des fichiers .d contenant des fragments Makefile sur les dépendances des en-têtes. La deuxième ligne les utilise simplement.Pour sûr, un Makefile bien écrit devrait également inclure
clean
et desdistclean
règles:Remarquez,
$(RM)
c'est l'équivalent derm -f
, mais c'est une bonne pratique de ne pas appelerrm
directement.La
all
règle est également appréciée. Pour fonctionner, cela devrait être la première règle de votre dossier:Vous pouvez également ajouter une
install
règle:DESTDIR
est vide par défaut. L'utilisateur peut le configurer pour installer votre programme sur un autre système (obligatoire pour le processus de compilation croisée). Les mainteneurs de packages pour la distribution multiple peuvent également changerPREFIX
afin d'installer votre package dans/usr
.Un dernier mot: ne placez pas les fichiers source dans des sous-répertoires. Si vous voulez vraiment le faire, conservez-le
Makefile
dans le répertoire racine et utilisez les chemins d'accès complets pour identifier vos fichiers (c'est-à-diresubdir/file.o
).Donc, pour résumer, votre Makefile complet devrait ressembler à:
la source
make
que je sais (GNU Make et BSD Make) n'a besoin de lignes vides entre les règles. Cependant, il existe des tonnes d'make
implémentations avec leurs propres bogues ^ Wspécificités.J'ai utilisé la réponse de friedmud . J'ai étudié la question pendant un certain temps et cela semble être un bon moyen de commencer. Cette solution dispose également d'une méthode bien définie pour ajouter des indicateurs de compilateur. J'ai répondu à nouveau, car j'ai apporté des modifications pour le faire fonctionner dans mon environnement, Ubuntu et g ++. Plus d'exemples de travail sont parfois le meilleur professeur.
Les Makefiles semblent être très complexes. J'en utilisais un, mais il générait une erreur liée à la non-liaison dans les bibliothèques g ++. Cette configuration a résolu ce problème.
la source