Comment puis-je créer un Makefile pour les projets C avec les sous-répertoires SRC, OBJ et BIN?

Il y a quelques mois, j'ai trouvé le générique Makefile suivant pour les devoirs scolaires:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# 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 fichiers .o et l'exécutable projectname dans le même dossier.

Maintenant, j'aimerais pousser ça un peu. Comment puis-je écrire un Makefile pour compiler un projet C avec la structure de répertoire suivante?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

En d'autres termes, j'aimerais avoir un Makefile qui compile les sources C de ./src/ dans ./obj/, puis lier tout 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; à la place, le projet ne parvient pas à compiler avec toutes sortes d'erreurs. Bien sûr, je pourrais utiliser L'IDE complet (Monodevelop, Anjuta, etc.), mais honnêtement, je préfère rester avec gEdit et le 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? Merci!!!!

** mise à JOUR (v4) **

La solution finale :

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# 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!"
70
demandé sur Yanick Rochon 2011-08-10 04:41:55

3 réponses

Tout d'abord, votre règle $(OBJECTS) est problématique, car:

  1. c'est un peu aveugle, faisant toutes les sources prérequis de chaque objet,
  2. , il utilise souvent la mauvaise source (comme vous l'avez découvert avec file1.o et file2.o)
  3. Il essaie de construire des exécutables au lieu de s'arrêter aux objets, et
  4. le nom de la cible (foo.o) n'est pas ce que la règle produira réellement (obj/foo.o).

Je suggère le suivant:

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 construit. Pour cette raison, si vous tapez make plusieurs fois, Make reconstruira la cible à chaque fois, même s'il n'y a aucune raison de le faire. Un petit changement corrige que:

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

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 reconstruire. Mais que peut attendre un autre jour.

modifier:
Désolé, j'ai omis une partie de la règle $(OBJECTS) ci-dessus; je l'ai corrigée. (J'aimerais pouvoir utiliser "strike" dans un exemple de code.)

25
répondu Beta 2017-11-07 16:02:57

Vous pouvez ajouter l'indicateur -I 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é:

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 l'option -o lors de la compilation. Aussi, regardez le $@ et $< variables automatiques .

Par exemple, considérez 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)/$@

Mise à jour >

En regardant votre makefile, Je réalisez que vous utilisez le drapeau -o. Bien. Continuez à l'utiliser, mais ajoutez une variable de répertoire cible pour indiquer où le fichier de sortie doit être écrit.

5
répondu Tom 2011-08-10 01:20:10

J'ai arrêté d'écrire des makefiles ces jours-ci, si votre intention est d'apprendre à aller de l'avant, sinon vous avez un bon générateur de makefile fourni avec eclipse CDT. Si vous voulez une certaine maintenabilité / support de projet multiple avec dans votre arbre de construction, jetez un oeil à ce qui suit -

Https://github.com/dmoulding/boilermake j'ai trouvé ça plutôt bien..!

1
répondu Kamath 2011-08-10 08:03:20