Il y a quelques mois, j'ai proposé le générique suivant Makefile
pour les devoirs scolaires:
# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# Date : 2010-11-05
#
# Changelog :
# 0.01 - first version
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc -std=c99 -c
# compiling flags here
CFLAGS = -Wall -I.
LINKER = gcc -o
# linking flags here
LFLAGS = -Wall
SOURCES := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS := $(SOURCES:.c=*.o)
rm = rm -f
$(TARGET): obj
@$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"
obj: $(SOURCES) $(INCLUDES)
@$(CC) $(CFLAGS) $(SOURCES)
@echo "Compilation complete!"
clean:
@$(rm) $(TARGET) $(OBJECTS)
@echo "Cleanup complete!"
Cela compilera essentiellement tous les fichiers .c
et .h
pour générer les .o
fichiers et l'exécutable projectname
dans le même dossier.
Maintenant, j'aimerais pousser un peu cela. Comment puis-je écrire un Makefile pour compiler un projet C avec la structure de répertoires suivante?
./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>
En d' autres termes, je voudrais avoir un Makefile qui compile les sources C de ./src/
dans ./obj/
, puis tout lien pour créer l'exécutable dans ./bin/
.
J'ai essayé de lire différents Makefiles, mais je ne peux tout simplement pas les faire fonctionner pour la structure du projet ci-dessus; au lieu de cela, le projet ne parvient pas à se compiler avec toutes sortes d'erreurs. Bien sûr, je pourrais utiliser un IDE complet (Monodevelop, Anjuta, etc.), mais je préfère honnêtement m'en tenir à gEdit et au bon vieux terminal.
Y a-t-il un gourou qui peut me donner une solution de travail ou des informations claires sur la façon dont cela peut être fait? Je vous remercie!
** MISE À JOUR (v4) **
La solution finale :
# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# Date : 2011-08-10
#
# Changelog :
# 2010-11-05 - first version
# 2011-08-10 - added structure : sources, objects, binaries
# thanks to http://stackoverflow.com/users/128940/beta
# 2017-04-24 - changed order of linker params
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc
# compiling flags here
CFLAGS = -std=c99 -Wall -I.
LINKER = gcc
# linking flags here
LFLAGS = -Wall -I. -lm
# change these to proper directories where each file should be
SRCDIR = src
OBJDIR = obj
BINDIR = bin
SOURCES := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm = rm -f
$(BINDIR)/$(TARGET): $(OBJECTS)
@$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
@echo "Linking complete!"
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
@$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"
.PHONY: clean
clean:
@$(rm) $(OBJECTS)
@echo "Cleanup complete!"
.PHONY: remove
remove: clean
@$(rm) $(BINDIR)/$(TARGET)
@echo "Executable removed!"
Makefile
. Je me rapproche, mais j'ai des problèmes avec les variables automatiques, donc il semble quand mêmeRéponses:
Premièrement, votre
$(OBJECTS)
règle est problématique, car:file1.o
etfile2.o
)foo.o
) n'est pas ce que la règle produira réellement (obj/foo.o
).Je suggère ce qui suit:
La
$(TARGET)
règle présente le même problème que le nom de la cible ne décrit pas réellement ce que la règle construit. Pour cette raison, si vous tapezmake
plusieurs fois, Make reconstruira la cible à chaque fois, même s'il n'y a aucune raison de le faire. Un petit changement corrige cela:Une fois que tout est en ordre, vous pouvez envisager une gestion des dépendances plus sophistiquée; si vous modifiez l'un des fichiers d'en-tête, ce makefile ne saura pas quels objets / exécutables doivent être reconstruits. Mais cela peut attendre un autre jour.
EDIT:
Désolé, j'ai omis une partie de la
$(OBJECTS)
règle ci-dessus; Je l'ai corrigé. (J'aimerais pouvoir utiliser "grève" dans un exemple de code.)la source
obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
main
fonctions? Peut-être un dansfile1.c
et un dansmain.c
? Si tel est le cas, vous ne pourrez pas lier ces objets; il ne peut y en avoir qu'unmain
dans un exécutable.file1.c
mais cela donne le même message à tous les fichiers du projet. Etmain.c
est le seul avec une fonction principale ... etmain.c
importefile1.h
etfile2.h
(il n'y a pas de relation entrefile1.c
etfile2.c
), mais je doute que le problème vienne de là.$(OBJECTS)
règle; Je l'ai édité. Avec la mauvaise ligne, j'ai eu une erreur, mais pas celle que vous avez ...Vous pouvez ajouter l'
-I
indicateur aux indicateurs du compilateur (CFLAGS) pour indiquer où le compilateur doit rechercher les fichiers source, et l'indicateur -o pour indiquer où le binaire doit être laissé:Afin de déposer les fichiers objets dans le
obj
répertoire, utilisez l'-o
option lors de la compilation. Regardez également les variables automatiques$@
et .$<
Par exemple, considérez ce Makefile simple
Mettre à jour>
En regardant votre makefile, je me rends compte que vous utilisez le
-o
drapeau. Bien. Continuez à l'utiliser, mais ajoutez une variable de répertoire cible pour indiquer où le fichier de sortie doit être écrit.la source
-l ...
auCFLAGS
et ... il y a déjà l'-o
argument du lien (LINKER
)make
, d'où se trouve le MakefileJ'ai arrêté d'écrire des makefiles ces jours-ci, si votre intention est d'apprendre, allez-y, sinon vous avez un bon générateur de makefile fourni avec eclipse CDT. Si vous voulez une certaine maintenabilité / prise en charge de plusieurs projets dans votre arborescence de construction, jetez un œil à ce qui suit -
https://github.com/dmoulding/boilermake J'ai trouvé ça plutôt bien ..!
la source