The 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
|
# "C" or "C++"
PROJECT_KIND := C
# "debug" or "release"
BUILD_KIND := debug
# change it as your want, default is "main"
EXEC := main
INC_DIR := include
SRC_DIR := src
BUILD_DIR := build
OBJ_DIR := $(BUILD_DIR)/obj
BIN_DIR := $(BUILD_DIR)/bin
DEP_DIR := $(BUILD_DIR)/dep
TARGET := $(BIN_DIR)/a.out
# compiler
CC := gcc
CXX := g++
# compiler flags
CFLAGS := -Wall -Wextra -std=c11
CXXFLAGS := -Wall -Wextra -Wconversion -Wshadow -std=c++17
# linker flags, like "-lm"
LDFLAGS :=
# header and source file extensions
C_HEADER_EXT := h
C_SOURCE_EXT := c
CXX_HEADER_EXT := hpp
CXX_SOURCE_EXT := cpp
HEADER_EXT :=
SOURCE_EXT :=
COMPILER :=
FLAGS :=
ifeq ($(PROJECT_KIND), C)
HEADER_EXT = $(C_HEADER_EXT)
SOURCE_EXT = $(C_SOURCE_EXT)
COMPILER = $(CC)
FLAGS = $(CFLAGS)
else
HEADER_EXT = $(CXX_HEADER_EXT)
SOURCE_EXT = $(CXX_SOURCE_EXT)
COMPILER = $(CXX)
FLAGS = $(CXXFLAGS)
endif
ifeq ($(BUILD_KIND), release)
FLAGS += -DNDEBUG
endif
# ask compiler to generate dependency files
DEPFLAGS += -MMD -MP -MT $@ -MF $(DEP_DIR)/$*.d
INC := $(wildcard $(INC_DIR)/*.$(HEADER_EXT))
SRC := $(wildcard $(SRC_DIR)/*.$(SOURCE_EXT))
OBJ := $(patsubst $(SRC_DIR)/%.$(SOURCE_EXT),$(OBJ_DIR)/%.o,$(SRC))
DEP := $(patsubst $(SRC_DIR)/%.$(SOURCE_EXT),$(DEP_DIR)/%.d,$(SRC))
.PHONY: all, setup, run, clean
all: $(TARGET)
@cp $(TARGET) $(EXEC)
$(TARGET): $(OBJ) | $(BIN_DIR)
$(info ld $@ $^)
@$(COMPILER) $(LDFLAGS) -o $@ $^
$(OBJ): $(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SOURCE_EXT) $(DEP_DIR)/%.d | $(OBJ_DIR) $(DEP_DIR)
$(info cc $@ $<)
@$(COMPILER) $(DEPFLAGS) $(FLAGS) -I$(INC_DIR) -c -o $@ $<
$(DEP):
include $(wildcard $(DEP))
$(DEP_DIR): ; [ -d $(DEP_DIR) ] || mkdir -p $(DEP_DIR)
$(OBJ_DIR): ; [ -d $(OBJ_DIR) ] || mkdir -p $(OBJ_DIR)
$(BIN_DIR): ; [ -d $(BIN_DIR) ] || mkdir -p $(BIN_DIR)
clean:
@$(RM) $(EXEC) $(TARGET) $(OBJ) $(DEP)
|
The makefile is aimed at providing a fast, convenient snippet for building "simple" C or C++ projects.
You are supposed to organize your project tree as follows:
.
├── build
│ ├── bin
│ ├── dep
│ └── obj
├── include
├── src
└── makefile
- Put headers in
include
directory.
- Put source files in
src
directory. The main.{c,cpp}
also stays in src
.
- All the compiled object files will be placed in
build/obj
directory.
- The final single executable file goes into
build/bin
directory, you will also get a copy of that in the project root.
- All the dependenciy files (with
.d
extension) generated by gcc stay in build/dep
directory.
Usage:
make
: Build the project, and necessary directories will be created.
make clean
: Clean all the object, dependency and executable files.
I am NOT going to explain all these freaking shits in this makefile, but you could JUST USE IT…
However, if you do have interests in those details, here are some keywords that might be helpful for you to search on the web:
- gnu makefile
- makefile rules
- makefile targets
- makefile variables
- makefile prerequisites
- makefile order-only prerequisites
- makefile functions
- makefile recipes
- makefile phony targets
- makefile automatic dependency generation
- makefile automatic variables
- makefile static pattern rules
Good luck and until next time!