-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathMakefile
131 lines (112 loc) · 4.42 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Global Variables
CXX := /usr/bin/g++
CXX_STANDERD := c++20
# Directories
BIN_DIR := ./bin
SRC_DIR := ./src
INC_DIR := ./include
BUILD_DIR := ./build
# Libraries
LIBS :=
# Find Source Codes, for example, ./src/hello_world.cpp
SRCS := $(shell find $(SRC_DIR) -name '*.cpp')
# Define Objective Files, for example, ./build/hello_world.cpp.o
# Prepends BUILD_DIR and appends .o to every src file
# $(var:pattern=replacement) is Makefile pattern substitution expression which substitute var to replacement according pattern
# % is used to match the stem, here % matches the whole string
# As an example, ./src/hello.cpp turns into ./build/./src/hello.cpp.o
OBJS := $(patsubst $(SRC_DIR)/%, $(BUILD_DIR)/%.o, $(SRCS))
# Define Dependencies Files, which are generated by gcc after first compilation
# for example, ./build/hello_world.cpp.d
DEPS := $(patsubst $(SRC_DIR)/%, $(BUILD_DIR)/%.d, $(SRCS))
# Define Exectuables, for example, ./bin/hello_world
EXES := $(patsubst $(SRC_DIR)/%.cpp, $(BIN_DIR)/%, $(SRCS))
# Flags
INC_DIR += $(shell find $(SRCS) -type d)
# Add a prefix to INC_DIR. So the folder moduleA would become -ImoduleA.
# Passing -ImoduleA to GCC, so that GCC will search the moduleA folder to find header files
INC_FLAGS := $(addprefix -I, $(INC_DIR))
# Similary, add prefix for libraries
LIB_FLAGS := $(addprefix -l, $(LIBS))
CXX_FLAGS := -Wall -MMD -MP -std=$(CXX_STANDERD) $(INC_FLAGS)
# The followings are Makefile Pattern Rules
#
# Pattern Rules are like:
# Target-Pattern: Prerequisite-Pattern
# <tab> command
#
# Target-Pattern is the pattern of the target file
# Target-Pattern is the pattern of the prerequisite file
#
# For example, the following rule is a Pattern Rule:
#
# %.o: %.c
# gcc -c $< -o $@
#
# For a give objective file, main.o, the rule matches main, so when exectuing, the rule turns to
#
# main.o: main.c
# gcc -c $< -o $@
#
# and $< and $@ are automatic variable, which represent current the first prerequisite and target file
# so finally, the after matching main.o, the rule turns to
#
# main.o: main.c
# gcc -c main.c -o main.o
#
#
# Then, the final problem is where are the target, like main.o, comes from to match the pattern rule?
# Like we know, DEPS := $(OBJS:%.o=.%d) will matches all element from OBJS, but where are the target comes from
# *The answer is the target is the prerequisite of final rule*
#
# For example, we have a final program hello, which has the folloing rule to built it:
#
# hello: hello.o print.o test.o
# gcc $^ -o $@
#
# $^ and $@ are automatic variable, which represent all prerequist and target, this is hello.o, print.o, test.o and hello
# So the hello rule means before making hello, we need to have hello.o, print.o and test.o
#
# If we don't have these files, the Make will find the rule that builds them,
# i.e. find the rules whose target are hello.o, print.o and test,o
# Since we don't have a plicit rule whose target are them, the Make will try the Pattern Rules
# And luckily, there is a rule that is:
#
# %.o: %.c
# gcc -c $< -o $@
#
# The pattern %.o matches hello.o, print.o and test.o. So this rule is called first to build hello rule's prerequisite
# Thus, the target used to match the Pattern Rule is actually come from other rule's prerequisite.
#
# Usually, we need a final rule that plicitly declare all objective files are prerequisite
# to enable the Make automatically matches Pattern Rule
# That is the rule below:
# Pattern Rules for Executables
$(EXES): $(BIN_DIR)/%: $(BUILD_DIR)/%.cpp.o
@mkdir -p $(dir $@)
$(CXX) $(CXX_FLAGS) $(LIB_FLAGS) $(LDFLAGS) -o $@ $<
# Pattern Rules for Executables
$(OBJS): $(BUILD_DIR)/%.o: $(SRC_DIR)/%
@mkdir -p $(dir $@)
$(CXX) $(CXX_FLAGS) -c $< -o $@
# Explicit Rules
# echo variables, for debug Makefile
echo:
$(foreach var, $(sort $(.VARIABLES)),\
$(if $(filter-out environment% default automatic,$(origin $(var))),\
$(info $(var): $($(var)))))
.PHONY: echo
# remove build cache
clean:
rm -rf $(BUILD_DIR) $(BIN_DIR)
.PHONY: clean
# build all binaries, the dependency $(EXEC) will match the pattern rules above
.DEFAULT_GOAL := all
all: $(EXES)
.PHONY: all
# Include the Makefiles generated by g++. Makefiles generated by g++ will have .d suffix
# and the content basically are just dependent headers of each files
# The - at the front suppresses the errors of missing Makefiles.
# Initially, all the .d files will be missing the first time of compiling, and we don't want those
# errors to show up.
-include $(DEPS)