Comment créer un Makefile C ++ SIMPLE

303

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?

Arriver à
la source
9
.EXE donc c'est définitivement Windows. À bien y penser ... le chemin est de style Unix. Probablement en utilisant Mingw-32.
Nathan Osman
2
Soupir. Je suppose que vous devez apprendre les bases de chaque métier, même si vous ne les utiliserez jamais. Il suffit de comprendre comment ça marche. Les chances sont bonnes, cependant, que vous développerez toujours dans un IDE, comme Eclipse. Vous obtiendrez une réponse ici pour votre cas simple d'une ligne et il y a beaucoup de tutoriels Web, mais si vous voulez des connaissances approfondies, vous ne pouvez pas battre le livre O'reilly (même chose pour la plupart des sujets s / w). amazon.com/Managing-Projects-Make-Nutshell-Handbooks/dp/… Choisissez une copie de seconde main sur amazon, half.com, betterworldbooks eBay
Mawg dit de rétablir Monica
2
Le lien publié par @Dennis est maintenant mort, mais le même matériel peut être trouvé dans cette page archive.org .
Guilherme Salomé
Je préfère les idées de cette personne. ( hiltmon.com/blog/2013/07/03/… ) La structure du projet peut être facilement modifiée pour convenir. Et je suis également d'accord pour que le temps du développeur soit consacré à autre chose qu'à automake / autoconf. Ces outils ont leur place, mais peut-être pas pour les projets internes. Je construis un script qui produira une telle structure de projet.
Daisuke Aramaki
@ GuilhermeSalomé Merci, je crois que c'est le meilleur tutoriel simple et complet.
Hareen Laks

Réponses:

562

Comme c'est pour Unix, les exécutables n'ont pas d'extensions.

Une chose à noter est qu'il root-configs'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.hhet support.oqui dépendent rootet 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'existant toolest désormais obsolète) et que vous souhaitez compiler le programme.

Pour le faire vous-même, vous pourriez

  1. Vérifiez si support.ccou support.hhest plus récent que support.o, et si c'est le cas, exécutez une commande comme

    g++ -g -c -pthread -I/sw/include/root support.cc
  2. Vérifiez si l'un support.hhou l' autre tool.ccest plus récent que tool.o, et si c'est le cas, exécutez une commande comme

    g++ -g  -c -pthread -I/sw/include/root tool.cc
  3. Vérifiez si tool.oest plus récent que tool, et si c'est le cas, exécutez une commande comme

    g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
    -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
    -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
    

Phew! 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é makefilecomme ceci:

tool: tool.o support.o
    g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
        -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
        -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

tool.o: tool.cc support.hh
    g++ -g  -c -pthread -I/sw/include/root tool.cc

support.o: support.hh support.cc
    g++ -g -c -pthread -I/sw/include/root support.cc

et tapez simplement makesur 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.ccchange, cela support.odoit être reconstruit, mais tool.opeut être laissé seul. Quand les support.omodifications tooldoivent ê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 (ou VAR := 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:

CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
       -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
       -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
       -lm -ldl

tool: tool.o support.o
    g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

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 de opatwith npatdans le texte.

Profiter de cela nous donne:

CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

tool: $(OBJS)
    g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh
    g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
    g++ $(CPPFLAGS) -c support.cc

qui est plus facile à taper et beaucoup plus lisible.

Remarquerez que

  1. Nous indiquons toujours explicitement les dépendances pour chaque fichier objet et l'exécutable final
  2. Nous avons dû taper explicitement la règle de compilation pour les deux fichiers source

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:

  1. règles de suffixe (considérées comme obsolètes dans la marque GNU, mais conservées pour des raisons de compatibilité descendante)
  2. règles implicites
  3. règles de modèle

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

%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -c $<

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 à utiliser
  • CXX - le compilateur C ++ à utiliser
  • LD - l'éditeur de liens à utiliser
  • CFLAGS - drapeau de compilation pour les fichiers source C
  • CXXFLAGS - 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 liens
  • LDLIBS - bibliothèques à relier

Un Makefile de base

En tirant parti des règles intégrées, nous pouvons simplifier notre makefile pour:

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh

support.o: support.hh support.cc

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) tool

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 cleansuppression des fichiers objet dans ce cas.

Nous avons toujours toutes les dépendances codées en dur.

Quelques améliorations mystérieuses

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
    $(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

depend: .depend

.depend: $(SRCS)
    $(RM) ./.depend
    $(CXX) $(CPPFLAGS) -MM $^>>./.depend;

clean:
    $(RM) $(OBJS)

distclean: clean
    $(RM) *~ .depend

include .depend

Remarquerez que

  1. Il n'y a plus de ligne de dépendance pour les fichiers source!?!
  2. Il y a une magie étrange liée à .depend et dépend
  3. Si vous le faites makealors ls -Avous voyez un fichier nommé .dependqui contient des choses qui ressemblent à faire des lignes de dépendance

Autre 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.)

dmckee --- chaton ex-modérateur
la source
9
Cette méthode pour générer des dépendances est obsolète et réellement nuisible. Voir Génération automatique de dépendances avancées .
Maxim Egorushkin
5
-pthreadflag permet gccde définir les macros nécessaires, -D_REENTRANTn'est pas nécessaire.
Maxim Egorushkin
8
@jcoe Il effectue une passe supplémentaire de préprocesseur inutile pour générer des dépendances. Faisant un travail inutile, il dissipe simplement la chaleur qui fait fondre les pôles de glace et, à plus grande échelle, approche de la mort thermique de notre univers.
Maxim Egorushkin
2
Probablement "nocif" est un peu trop, mais étant donné que les phases ou cibles de génération de dépendance explicites sont dépassées car au moins GCC 3, je pense vraiment que nous devrions tous les dépasser. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail pleure les démissionnaires
2
En réalité, la réponse acceptée ne devrait pas dépendre d'un logiciel très spécifique ( 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.
diode verte
56

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):

a3driver: a3driver.o
    g++ -o a3driver a3driver.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

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 à:

a3driver: a3driver.cpp
    g++ -o a3driver a3driver.cpp

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):

a3driver: a3driver.o secondFile.o
    g++ -o a3driver a3driver.o secondFile.o

a3driver.o: a3driver.cpp
    g++ -c a3driver.cpp

secondFile.o: secondFile.cpp secondFile.h
    g++ -c secondFile.cpp

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:

all: a3driver ;

clean:
    # -f so this will succeed even if the files don't exist
    rm -f a3driver a3driver.o

EDIT: Je n'ai pas remarqué que vous êtes sous Windows. Je pense que la seule différence est de changer le -o a3driveren -o a3driver.exe.

Brendan Long
la source
Le code absolu que j'essaie d'utiliser est: p4a.exe: p4driver.cpp g ++ -o p4a p4driver.cpp MAIS, il me dit "séparateur manquant". J'utilise TAB, mais cela me dit toujours cela. Une idée?
Befall
2
Pour autant que je sache, ce message d'erreur n'apparaît que si vous avez des espaces. Assurez-vous que vous n'avez pas de lignes commençant par des espaces (espace + tabulation donnera cette erreur). C'est la seule chose à laquelle je peux penser ..
Brendan Long
Remarque pour les futurs éditeurs: StackOverflow ne peut pas rendre les onglets même si vous les modifiez dans la réponse, alors n'essayez pas de "corriger" ma note à ce sujet.
Brendan Long
35

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 .Cfichiers puis de taper make...

appname := myapp

CXX := clang++
CXXFLAGS := -std=c++11

srcfiles := $(shell find . -name "*.C")
objects  := $(patsubst %.C, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend
friedmud
la source
2
Une raison pour ne pas rechercher automatiquement les fichiers source est que l'on peut avoir différentes cibles de génération nécessitant des fichiers différents.
hmijail pleure les démissionnaires
D'accord @hmijail, ainsi que des sous-modules qui contiennent une tonne de sources / en-têtes que vous ne voulez pas compiler / lier ... et sans aucun doute de nombreuses autres circonstances où une recherche / utilisation exhaustive est inappropriée.
Ingénieur
Pourquoi utiliser à la place "shell find" et non "wildcard"?
Nolan
1
@Nolan pour trouver les fichiers source dans une arborescence de répertoires source
AlejandroVD
13

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:

nmake a3a.exe

Et c'est tout. Si vous utilisez GNU Make, utilisez "make" ou "gmake" ou autre chose.

Option 2: un makefile à 2 lignes.

a3a.exe: a3driver.obj
    link /out:a3a.exe a3driver.obj
Personne
la source
3
Ce serait une excellente réponse si elle ne supposait pas tant de choses sur les détails de l'environnement du PO. Oui, ils sont sous Windows, mais cela ne signifie pas qu'ils utilisent nmake. La linkligne de commande semble également très spécifique à un compilateur particulier, et devrait à tout le moins documenter lequel.
tripleee
6

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):

main_target : source1 source2 etc
    command to build main_target from sources

source1 : dependents for source1
    command to build source1

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.

a3a.exe : a3driver.obj 
    link /out:a3a.exe a3driver.obj

a3driver.obj : a3driver.cpp
    cc a3driver.cpp
John Knoeller
la source
6

Je suggère (notez que le tiret est un TAB):

tool: tool.o file1.o file2.o
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

ou

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o

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.cet tool).

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 Makefilefonctionne 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 ++. Nous LINK.one modifions que pour cela. Si vous souhaitez compiler du code C, vous n'avez pas à forcer la LINK.ovaleur.

Bien sûr, vous pouvez également ajouter vos indicateurs de compilation avec variable CFLAGSet ajouter vos bibliothèques LDLIBS. Par exemple:

CFLAGS = -Wall
LDLIBS = -lm

Remarque: si vous devez utiliser des bibliothèques externes, je suggère d' utiliser pkg-config afin de définir correctement CFLAGSet LDLIBS:

CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)

Le lecteur attentif remarquera que cela Makefilene se reconstruit pas correctement si un en-tête est modifié. Ajoutez ces lignes pour résoudre le problème:

override CPPFLAGS += -MMD
include $(wildcard *.d)

-MMDpermet 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 cleanet des distcleanrègles:

clean:
    $(RM) *.o *.d

distclean: clean
    $(RM) tool

Remarquez, $(RM)c'est l'équivalent de rm -f, mais c'est une bonne pratique de ne pas appeler rmdirectement.

La allrègle est également appréciée. Pour fonctionner, cela devrait être la première règle de votre dossier:

all: tool

Vous pouvez également ajouter une installrègle:

PREFIX = /usr/local
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin

DESTDIRest 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 changer PREFIXafin 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 Makefiledans le répertoire racine et utilisez les chemins d'accès complets pour identifier vos fichiers (c'est-à-dire subdir/file.o).

Donc, pour résumer, votre Makefile complet devrait ressembler à:

LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)

all: tool
tool: tool.o file1.o file2.o
clean:
    $(RM) *.o *.d
distclean: clean
    $(RM) tool
install:
    install -m 755 tool $(DESTDIR)$(PREFIX)/bin
Jérôme Pouiller
la source
Vers la fin: ne devrait-il pas y avoir de lignes vides entre les règles? La réponse de John Knoeller l'a affirmé.
Peter Mortensen
Aucune implémentation de ce makeque je sais (GNU Make et BSD Make) n'a besoin de lignes vides entre les règles. Cependant, il existe des tonnes d' makeimplémentations avec leurs propres bogues ^ Wspécificités.
Jérôme Pouiller
5

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.

appname := myapp

CXX := g++
CXXFLAGS := -Wall -g

srcfiles := $(shell find . -maxdepth 1 -name "*.cpp")
objects  := $(patsubst %.cpp, %.o, $(srcfiles))

all: $(appname)

$(appname): $(objects)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) $(objects) $(LDLIBS)

depend: .depend

.depend: $(srcfiles)
    rm -f ./.depend
    $(CXX) $(CXXFLAGS) -MM $^>>./.depend;

clean:
    rm -f $(objects)

dist-clean: clean
    rm -f *~ .depend

include .depend

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.

VectorVortec
la source