Wie kann ich ein Makefile für C-Projekte mit den Unterverzeichnissen SRC, OBJ und BIN erstellen?

Lesezeit: 7 Minuten

Benutzeravatar von Yanick Rochon
Yanik Rochon

Vor ein paar Monaten kam ich auf das folgende Generikum Makefile für Schulaufgaben:

# ------------------------------------------------
# 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!"

Dies wird im Grunde jede kompilieren .c und .h Datei zu generieren .o Dateien und die ausführbare Datei projectname alles im selben Ordner.

Nun, ich möchte das ein wenig vorantreiben. Wie kann ich ein Makefile schreiben, um ein C-Projekt mit der folgenden Verzeichnisstruktur zu kompilieren?

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

Mit anderen Worten, ich hätte gerne ein Makefile, aus dem C-Quellen kompiliert werden ./src/ hinein ./obj/ und verknüpfen Sie dann alles, um die ausführbare Datei zu erstellen ./bin/.

Ich habe versucht, verschiedene Makefiles zu lesen, aber ich kann sie einfach nicht für die obige Projektstruktur zum Laufen bringen; Stattdessen kann das Projekt nicht mit allen möglichen Fehlern kompiliert werden. Sicher, ich könnte eine vollwertige IDE (Monodevelop, Anjuta usw.) verwenden, aber ich bleibe ehrlich gesagt lieber bei gEdit und dem guten alten Terminal.

Gibt es einen Guru, der mir eine funktionierende Lösung oder klare Informationen darüber geben kann, wie dies bewerkstelligt werden kann? Vielen Dank!

** AKTUALISIEREN (v4) **

Das finale Resultat :

# ------------------------------------------------
# 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!"

  • Was ist hier die konkrete Frage?

    – Oliver Charlesworth

    10. August 2011 um 0:44 Uhr

  • Ich bin mir nicht sicher, ob ich verstehe, was Sie tun möchten.

    – Tom

    10. August 2011 um 0:44 Uhr

  • Aktualisiert die Makefile. Ich bin nah dran, aber ich habe Probleme mit den automatischen Variablen, so scheint es jedenfalls

    – Yanik Rochon

    10. August 2011 um 3:06 Uhr

  • Ich habe gerade eine Lösung gefunden. Wenn jemand etwas Besseres finden möchte, kann das Makefile noch verbessert werden.

    – Yanik Rochon

    10. August 2011 um 4:05 Uhr

  • @YanickRochon Ich wollte deine Englischkenntnisse nicht kritisieren. Aber damit die PHONY-Ziele einen Sinn ergeben, können Sie definitiv nicht BANANA schreiben;) gnu.org/software/make/manual/html_node/Phony-Targets.html

    – Joni

    10. März 2016 um 5:54 Uhr

Benutzeravatar von Beta
Beta

Zuerst dein $(OBJECTS) Regel ist problematisch, weil:

  1. es ist irgendwie wahllos, zu machen alle Quellenvoraussetzungen für jedes Objekt,
  2. es wird oft verwendet die falsche Quelle (wie Sie mit entdeckt haben file1.o und file2.o)
  3. es versucht, ausführbare Dateien zu erstellen, anstatt bei Objekten anzuhalten, und
  4. der Name des Ziels (foo.o) ist nicht das, was die Regel tatsächlich erzeugt (obj/foo.o).

Ich schlage Folgendes vor:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

Das $(TARGET) Regel hat das gleiche Problem, dass der Zielname nicht wirklich beschreibt, was die Regel erstellt. Aus diesem Grund, wenn Sie tippen make mehrere Male erstellt Make das Ziel jedes Mal neu, obwohl es keinen Grund dafür gibt. Eine kleine Änderung behebt das:

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

Sobald das alles in Ordnung ist, könnten Sie eine ausgefeiltere Behandlung von Abhängigkeiten in Erwägung ziehen; Wenn Sie eine der Header-Dateien ändern, weiß dieses Makefile nicht, welche Objekte/ausführbaren Dateien neu erstellt werden müssen. Aber das kann noch einen Tag warten.

BEARBEITEN:

Entschuldigung, ich habe einen Teil weggelassen $(OBJECTS) Regel oben; Ich habe es korrigiert. (Ich wünschte, ich könnte „strike“ in einem Codebeispiel verwenden.)

  • Mit Ihren Änderungsvorschlägen bekomme ich: 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

    – Yanik Rochon

    10. August 2011 um 5:18 Uhr


  • @Yanick Rochon: Hast du mehrere main Funktionen? Vielleicht eins drin file1.c und eins drin main.c? In diesem Fall können Sie diese Objekte nicht verknüpfen. es kann nur einen geben main in einer ausführbaren Datei.

    – Beta

    10. August 2011 um 5:49 Uhr


  • Nein, ich weiß nicht. Alles funktioniert gut mit der letzten Version, die ich in der Frage gepostet habe. Wenn ich mein Makefile auf das ändere, was Sie vorschlagen (und ich verstehe die Vorteile dessen, was Sie sagen), bekomme ich das. Ich habe gerade eingefügt file1.c aber es gibt die gleiche Nachricht an alle einzelnen Dateien des Projekts. Und main.c ist der nur eine mit einer Hauptfunktion … und main.c Importe file1.h und file2.h (es gibt keinen Zusammenhang zwischen file1.c und file2.c), aber ich bezweifle, dass das Problem von dort kommt.

    – Yanik Rochon

    10. August 2011 um 6:25 Uhr


  • @Yanick Rochon: Ich habe einen Fehler beim Einfügen der ersten Zeile meiner gemacht $(OBJECTS) Regel; Ich habe es bearbeitet. Bei der schlechten Zeile habe ich einen Fehler bekommen, aber nicht den, den Sie bekommen haben …

    – Beta

    10. August 2011 um 12:48 Uhr

Toms Benutzeravatar
Tom

Sie können die hinzufügen -I Flag zu den Compiler-Flags (CFLAGS), um anzugeben, wo der Compiler nach Quelldateien suchen soll, und das Flag -o, um anzugeben, wo die Binärdatei gelassen werden soll:

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

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

Um die Objektdateien in die obj Verzeichnis verwenden Sie die -o Option beim Kompilieren. Schauen Sie sich auch die an $@ und $< automatische Variablen.

Betrachten Sie zum Beispiel dieses einfache Makefile

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

Aktualisieren>

Wenn ich mir Ihr Makefile ansehe, erkenne ich, dass Sie die verwenden -o Flagge. Gut. Verwenden Sie es weiterhin, aber fügen Sie eine Zielverzeichnisvariable hinzu, um anzugeben, wohin die Ausgabedatei geschrieben werden soll.

  • könntest du genauer werden? Meinst du hinzufügen -l ... zum CFLAGS und … da ist schon die -o Argument für den Linker (LINKER)

    – Yanik Rochon

    10. August 2011 um 1:03 Uhr


  • Ja, die CFLAGS, und ja, verwenden Sie weiterhin -o, fügen Sie einfach die Variable TARGETPATH ​​hinzu.

    – Tom

    10. August 2011 um 1:07 Uhr

  • Danke, ich habe die Änderungen vorgenommen, aber es scheint, dass mir noch etwas fehlt (siehe das Update zu der Frage)

    – Yanik Rochon

    10. August 2011 um 1:16 Uhr

  • nur makewo sich das Makefile befindet

    – Yanik Rochon

    10. August 2011 um 1:25 Uhr

  • Können Sie den ausgeführten Befehl nicht lesen? zum Beispiel gcc -c yadayada. Ziemlich sicher, dass es eine Variable gibt, die nicht das enthält, was Sie erwarten

    – Tom

    10. August 2011 um 1:41 Uhr

Ich habe heutzutage aufgehört, Makefiles zu schreiben, wenn Sie lernen möchten, machen Sie weiter, sonst haben Sie einen guten Makefile-Generator, der mit Eclipse CDT geliefert wird. Wenn Sie eine gewisse Wartbarkeit / Unterstützung mehrerer Projekte in Ihrem Build-Baum wünschen, werfen Sie einen Blick auf Folgendes –

https://github.com/dmoulding/boilermake Das fand ich ziemlich gut..!

  • Meinung basiert. Beantwortet nicht die Frage von OP. Geht von einer Eclipse-Umgebung aus.

    – Nathaniel Johnson

    2. Dezember 2017 um 14:27 Uhr

1421180cookie-checkWie kann ich ein Makefile für C-Projekte mit den Unterverzeichnissen SRC, OBJ und BIN erstellen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy