Il y a quelques mois, j'ai proposé le générique suivant Makefile
pour les travaux 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 va essentiellement compiler chaque .c
et .h
fichier pour générer .o
fichiers et l'exécutable projectname
tous dans le même dossier.
Maintenant, j'aimerais pousser ceci un peu. 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, j'aimerais avoir un Makefile qui compile les sources C à partir de ./src/
en ./obj/
puis liez tout pour créer le fichier 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 de projet ci-dessus; au lieu de cela, le projet ne parvient pas à compiler avec toutes sortes d'erreurs. Bien sûr, je pourrais utiliser à part entière IDE (Monodevelop, Anjuta, etc.), mais honnêtement, je préfère rester avec gEdit et le bon vieux terminal.
Y at-il un gourou qui peut me donner une solution de travail, ou des informations claires sur la façon dont cela peut être fait? Merci!
** PDATE (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!"
Premièrement, votre règle $(OBJECTS)
est problématique, car:
file1.o
et file2.o
)foo.o
) n'est pas ce que la règle produira réellement (obj/foo.o
).Je suggère ce qui suit:
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"
La règle $(TARGET)
a le même problème que le nom de la cible ne décrit pas réellement ce que la règle génère. Pour cette raison, si vous tapez make
plusieurs fois, Make reconstruira la cible à chaque fois, même s’il n’ya aucune raison de le faire. Un petit changement corrige cela:
$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $@ $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"
Une fois que tout est en ordre, vous pouvez envisager une gestion de la dépendance plus sophistiquée; Si vous modifiez l'un des fichiers d'en-tête, ce fichier make ne saura pas quels objets/exécutables doivent être reconstruits. Mais cela peut attendre un autre jour.
MODIFIER:
Désolé, j'ai omis une partie de la règle $(OBJECTS)
ci-dessus; Je l'ai corrigé. (J'aimerais pouvoir utiliser "strike" dans un exemple de code.)
Vous pouvez ajouter le -I
drapeau sur les drapeaux du compilateur (CFLAGS) pour indiquer où le compilateur doit chercher les fichiers source, et le drapeau -o pour indiquer où le binaire doit être laissé:
CFLAGS = -Wall -I./src
TARGETPATH = ./bin
$(TARGET): obj
@$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"
Pour déposer les fichiers objet dans le répertoire obj
, utilisez le fichier -o
option lors de la compilation. Aussi, regardez le $@
et $<
variables automatiques .
Par exemple, considérons ce Makefile simple
CFLAGS= -g -Wall -O3
OBJDIR= ./obj
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@
Mettre à jour>
En regardant votre makefile, je réalise 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.
J'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 un support pour la maintenabilité/plusieurs projets dans votre arbre de compilation, regardez ce qui suit -
https://github.com/dmoulding/boilermake J'ai trouvé cela plutôt bien ..!