Folgend dieses Tutorial…
Ich habe 2 Quelldateien und 1 Header-Datei. Ich möchte sie in separaten Verzeichnissen wie im Tutorial haben.
Also habe ich dieses Projekt aufgesetzt:
.
├── include
│ └── hellomake.h
├── Makefile
└── src
├── hellofunc.c
└── hellomake.c
Makefile:
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o [email protected] $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o [email protected] $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
Der Fehler, den ich generiere, sagt:
gcc -o hellomake -I../include
gcc: fatal error: no input files
compilation terminated.
make: *** [hellomake] Error 4
Was ist los?
Ihr Tutorial fördert alte und schlechte Praktiken, Sie sollten es meiner Meinung nach vermeiden.
In Ihrer Regel hier:
$(ODIR)/%.o: %.c $(DEPS)
Sie sagen make, dass es im aktuellen Verzeichnis nach Quellen suchen soll, während sie sich tatsächlich im befinden src
Verzeichnis, daher wird dieses Muster nie verwendet und Sie haben kein passendes.
Stellen Sie sicher, dass Sie Ihr Projektverzeichnis wie folgt organisieren:
root
├── include/
│ └── all .h files here
├── lib/
│ └── all third-party library files (.a/.so files) here
├── src/
│ └── all .c files here
└── Makefile
Lassen Sie uns dann den Prozess Schritt für Schritt durchgehen und bewährte Verfahren anwenden.
Erstens: Definieren Sie nichts, wenn Sie es nicht müssen. Make hat viele vordefinierte Variablen und Funktionen, die Sie verwenden sollten, bevor Sie versuchen, es manuell zu tun. Tatsächlich hat er so viele, dass Sie eine einfache Datei kompilieren können, ohne überhaupt ein Makefile im Verzeichnis zu haben!
-
Listen Sie Ihre Quell- und Build-Ausgabeverzeichnisse auf:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin # or . if you want it in the current directory
-
Nennen Sie Ihr endgültiges Ziel, d. h. Ihre ausführbare Datei:
EXE := $(BIN_DIR)/hellomake
-
Listen Sie Ihre Quelldateien auf:
SRC := $(wildcard $(SRC_DIR)/*.c)
-
Listen Sie in den Quelldateien die Objektdateien auf:
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
# You can also do it like that
OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC))
-
Jetzt kümmern wir uns um die Flags
CPPFLAGS := -Iinclude -MMD -MP # -I is a preprocessor flag, not a compiler flag
CFLAGS := -Wall # some warnings about bad code
LDFLAGS := -Llib # -L is a linker flag
LDLIBS := -lm # Left empty if no libs are needed
(CPP
steht für C PbetreffendPProzessor hier, nicht CPlusPlus! Verwenden CXXFLAGS
für C++-Flags und CXX
für C++-Compiler.)
Das -MMD -MP
Flags werden verwendet, um die Header-Abhängigkeiten automatisch zu generieren. Wir werden dies später verwenden, um eine Kompilierung auszulösen, wenn sich nur ein Header ändert.
Ok, jetzt, da unsere Variablen korrekt gefüllt sind, ist es an der Zeit, ein paar Rezepte zu erstellen.
Es ist weit verbreitet, dass das Standardziel aufgerufen werden soll all
und dass es das erste Ziel in Ihrem Makefile sein sollte. Seine Voraussetzungen sollten das Ziel sein, das Sie nur beim Schreiben erstellen möchten make
auf der Kommandozeile:
all: $(EXE)
Ein Problem ist jedoch, dass Make denkt, dass wir tatsächlich eine Datei oder einen Ordner mit dem Namen erstellen möchten all
sagen wir ihm also, dass dies kein echtes Ziel ist:
.PHONY: all
Listen Sie nun die Voraussetzungen zum Erstellen Ihrer ausführbaren Datei auf und füllen Sie das Rezept aus, um make mitzuteilen, was damit zu tun ist:
$(EXE): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o [email protected]
(CC
steht für C CCompiler.)
Beachten Sie, dass Ihre $(BIN_DIR)
möglicherweise noch nicht vorhanden, sodass der Aufruf des Compilers möglicherweise fehlschlägt. Sagen wir make, dass es zuerst danach suchen soll:
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o [email protected]
$(BIN_DIR):
mkdir -p [email protected]
Einige schnelle zusätzliche Anmerkungen:
$(CC)
ist eine eingebaute Variable, die bereits alles enthält, was Sie beim Kompilieren und Linken in C benötigen
- Um Linkerfehler zu vermeiden, wird dringend empfohlen, put
$(LDFLAGS)
Vor Ihre Objektdateien und $(LDLIBS)
nach
$(CPPFLAGS)
und $(CFLAGS)
sind nicht zu gebrauchen hier ist die Kompilationsphase schon vorbei, hier ist die Verlinkungsphase
Da Ihre Quell- und Objektdateien nicht dasselbe Präfix haben, müssen Sie im nächsten Schritt make genau sagen, was zu tun ist, da die integrierten Regeln Ihren speziellen Fall nicht abdecken:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o [email protected]
Gleiches Problem wie zuvor, Ihr $(OBJ_DIR)
möglicherweise noch nicht vorhanden, sodass der Aufruf des Compilers möglicherweise fehlschlägt. Aktualisieren wir die Regeln:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o [email protected]
$(BIN_DIR) $(OBJ_DIR):
mkdir -p [email protected]
Ok, jetzt sollte die ausführbare Datei schön gebaut werden. Wir wollen jedoch eine einfache Regel, um die Build-Artefakte zu bereinigen:
clean:
@$(RM) -rv $(BIN_DIR) $(OBJ_DIR) # The @ disables the echoing of the command
(Auch hier ist clean kein Ziel, das erstellt werden muss, also fügen Sie es der .PHONY
Sonderziel!)
Letztes Ding. Erinnern Sie sich an die automatische Generierung von Abhängigkeiten? GCC und Clang erstellen .d
Dateien, die Ihren entsprechen .o
files, die Makefile-Regeln enthält, die wir verwenden können, also fügen wir das hier ein:
-include $(OBJ:.o=.d) # The dash silences errors when files don't exist (yet)
Endergebnis:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
EXE := $(BIN_DIR)/hellomake
SRC := $(wildcard $(SRC_DIR)/*.c)
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
CPPFLAGS := -Iinclude -MMD -MP
CFLAGS := -Wall
LDFLAGS := -Llib
LDLIBS := -lm
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o [email protected]
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o [email protected]
$(BIN_DIR) $(OBJ_DIR):
mkdir -p [email protected]
clean:
@$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
Das Dienstprogramm make erstellt ohne spezifisches ‘Ziel’ das erste Ziel in der Datei.
Das erste Ziel heißt normalerweise “alle”.
Für die bereitgestellte Datei werden die Objektdateien erstellt und die ausführbare Datei wird nicht fortgesetzt, wenn das Ziel nicht in der Befehlszeile angegeben wird
Schlagen Sie Folgendes vor:
SHELL := /bin/sh
# following so could define executable name on command line
# using the '-D' parameter
#ifndef $(NAME)
NAME := hellomake
#endif
# use ':=' so macros only evaluated once
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CFLAGS := -g -Wall -Wextra -pedantic
LFLAGS :=
ODIR := obj
IDIR := ../include
LIBS :=
LIBPATH := ../lib
DEPS := $(wildcard $(IDIR)/*.h)
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
.PHONY: all
all: $(NAME) $(OBJS)
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o [email protected] $< -I$(DEPS)
$(NAME): $(OBJS)
$(CC) $(LFLAGS) -o [email protected] $^ -L$(LIBPATH) -l$(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o
rm -f $(NAME)
however, in your proposed project,
not every source file needs every header file
so should use either gcc or sed to generate the dependency files
then use makefile rules similar to the following,
which may need a little 'tweaking' for your project
because the include files are not in the same directory
as the source files:
DEP := $(SRCS:.c=.d)
#
#create dependency files
#
%.d: %.c
#
# ========= START $< TO [email protected] =========
$(CC) -M $(CPPFLAGS) $< > [email protected]$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected]$$$$ > [email protected]; \
rm -f [email protected]$$$$
# ========= END $< TO [email protected] =========
#
# compile the .c files into .o files using the compiler flags
#
%.o: %.c %.d
#
# ========= START $< TO [email protected] =========
$(CC) $(CCFLAGS) -c $< -o [email protected] -I$(IDIR)
# ========= END $< TO [email protected] =========
#
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependencies for that .c file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif
Die einfachen Makefile-Definitionen erscheinen mir in Ordnung, wie sie in Ihrer Frage erscheinen. Versuchen Sie, die Compileroptionen vor den Dateinamen anzugeben:
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o [email protected] $<
hellomake: $(OBJ)
gcc $(CFLAGS) -o [email protected] $^
Du musst laufen make
aus dem Quellverzeichnis.
Wenn Sie diesen Fehler erhalten haben”
*gcc: fatal error: no input files
compilation terminated.*
“, das heißt, Sie haben keine Objektdateien,
schau dir einfach diese Zeile an “${OBJS} :=
” im Makefile.
Hi Bruder!
Wenn Ihr Projekt “helloFunc
” ‘s Architektur gefällt einfach so:
helloFunc
|
|__Makefile
|__build
|__include
| |__hellomake.h
|__src
|__hellofunc.cpp
|__hellomake.cpp
Ihr Makefile sollte genau so aussehen:
# This is a Makefile for separated multiple sources to build with VSCode on mac
# Thanks, Job Vranish.
# (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
# Reference: Makefile Tutorial
# (https://makefiletutorial.com/)
# Reference: @yagiyuki from Qiita
# (https://qiita.com/yagiyuki/items/ff343d381d9477e89f3b)
# Reference: simonsso from Github
# (https://github.com/simonsso/empty-cpp-project/blob/master/Makefile)
# Reference: Chinese Website blogger CDNS
# (https://blog.csdn.net/qq_22073849/article/details/88893201)
# (1)Compiler
# clang++
CXX = clang++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, clang make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o [email protected]
# (15)Compile: Generate object files from source files
# [email protected] := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir [email protected])
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o [email protected]
#(16)Delete dependence files, object files, and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
Ändern Sie dieses Makefile in Ihre benötigte Linux-Version:
# (1)Compiler
# g++
CXX = g++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, gcc make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o [email protected]
# (15)Compile: Generate object files from source files
# [email protected] := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir [email protected])
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o [email protected]
#(16)Delete dependency files, object files and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
Was Sie beachten müssen, ist, dass Ihr “Makefile
” Datei ist das gleiche Verzeichnis der Include-Dateien und Quelldateien,
Sie müssen also Ihre ändern “IDIR:=../include
” in “IDIR:=./include” in Ihrem “Makefile
“.
ENDE!
Poste deinen eigentlichen Code und formatiere ihn so, dass er lesbar ist
– Eregrith
1. Juni 2015 um 12:39 Uhr
Ich glaube nicht, dass ich gcc eine Datei zur Verfügung stellen sollte, alles ist im Makefile enthalten und alles, was ich aufrufe, ist make im Verzeichnis, da alle 3 Dateien bereits im Makefile enthalten sind
– iBeyondPower
1. Juni 2015 um 12:43 Uhr
Ein Trick, den ich beim Debuggen einer Make-Datei nützlich finde, besteht darin, ein “show” -Ziel (oder einen beliebigen Namen) ohne Abhängigkeiten einzurichten, das nur Ihre Variablen druckt, dh:
@echo "OBJ=$(OBJ)"
(eine Zeile für jede Variable). Dann mitmake show
Sie werden deutlich sehen, ob sie den richtigen Inhalt enthalten.– Kebs
1. Juni 2015 um 12:49 Uhr
Der Zweck eines Makefiles besteht darin, Befehle aufzurufen. Einer davon ist
gcc
zum Beispiel. Wenn Sie nicht lieferngcc
mit den zu verknüpfenden Dateien wird es sie nicht erfinden.– Eregrith
1. Juni 2015 um 12:52 Uhr
$info
kann fast überall in Ihrem Makefile platziert werden, wohingegenecho
kann nur in der Variablendeklaration oder im Regeltext platziert werden.– Chnossos
2. Juni 2015 um 21:19 Uhr