-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMakefile
executable file
·240 lines (189 loc) · 7.77 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# This is a generic makefile (works only on Linux operating system).
# It finds all files with extension .c inside src/ folder and compiles
# them into object files with extension .o and saves them in the folder
# release/.obj. At the same time, dependency files .d are saved in the
# release/.deps folder. These files are generated by the compiler and
# contain information about the header files .h that are included directly
# and indirectly by the .c file in question. On the next call to make, it
# will check if there has been any change in the .c file or in its
# dependency files and if so, it will compile the corresponding object
# file again. After all object files are generated, the linker will merge
# them all into an executable file (with the same name as the project
# folder). This approach allows any level of subfolders in the src/ folder.
# If any .c files are created inside the src/ folder, make will detect it
# automatically and include them in the compilation list. If any .c file
# is excluded, its corresponding object file .o will not be excluded, but
# it will not be included in the linker's list either, without causing
# major problems. If we modify this makefile, the whole project will be
# recompiled when we call make again.
# ----------------------------------------
# Project definitions
# ----------------------------------------
# Name of the project (same as the basename of the current directory)
PROJECT := $(shell basename "$(PWD)")
# Folders
RELEASE_DIR := release
DEBUG_DIR := debug
ODIR := .obj
DDIR := .deps
SDIR := src
# ----------------------------------------
# Compiler and linker definitions
# ----------------------------------------
# Compiler, linker and debugger
ifndef CC
CC := gcc
endif
DEBUGGER := gdb
# Git version
# Based on: https://stackoverflow.com/questions/1704907/how-can-i-get-my-c-code-to-automatically-print-out-its-git-version-hash
GIT_VERSION := "$(shell git describe --always --dirty --tags)"
# Flags for compiler
COMMON_FLAGS := -W -Wall -Wextra -pedantic -Wconversion -Wswitch-enum -Werror -flto -std=c11
RELEASE_FLAGS := -O2
RELEASE_DEFS := -DPROJECT=\"$(PROJECT)\" -DVERSION=\"$(GIT_VERSION)\"
DEBUG_FLAGS := -O0 -g
DEBUG_DEFS := -DPROJECT=\"$(PROJECT)\" -DVERSION=\"$(GIT_VERSION)\" -DDEBUG
LINK_FLAGS := -flto
# Libraries
LIBS := -lm
# ----------------------------------------
# Cross-platform tools
# ----------------------------------------
# Determine the platform
ifeq ($(OS), Windows_NT)
PLATFORM := Windows
else
PLATFORM := $(shell uname -s)
endif
# Executable suffix is .exe for windows target
ifeq (,$(findstring mingw,$(CC))$(findstring Windows,$(PLATFORM)))
SUFFIX :=
else
SUFFIX := .exe
endif
# Create a directory
ifndef MKDIR_P
MKDIR_P := mkdir -p
endif
# Move or rename a file
ifndef MOVE
MOVE := mv -f
endif
# Delete a directory
ifndef RM
RM := rm -f
endif
# Delete a directory
ifndef TOUCH
TOUCH := touch
endif
# ----------------------------------------
# Project macros and functions
# ----------------------------------------
# Recursive wildcard
# Based on: https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make
rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
# Name of the outputs of each rule
RELEASE_EXEC := $(RELEASE_DIR)/$(PROJECT)$(SUFFIX)
DEBUG_EXEC := $(DEBUG_DIR)/$(PROJECT)$(SUFFIX)
# Source files
SRCS := $(call rwildcard,$(SDIR),*.c)
# Header files
INCS := $(call rwildcard,$(SDIR),*.h)
# Dependency files (auto generated)
DEPS := $(patsubst %,%.d,$(basename $(subst $(SDIR),$(DDIR),$(SRCS))))
RELEASE_DEPS := $(addprefix $(RELEASE_DIR)/, $(DEPS))
DEBUG_DEPS := $(addprefix $(DEBUG_DIR)/, $(DEPS))
# Object files
OBJS := $(patsubst %,%.o,$(basename $(subst $(SDIR),$(ODIR),$(SRCS))))
RELEASE_OBJS := $(addprefix $(RELEASE_DIR)/, $(OBJS))
DEBUG_OBJS := $(addprefix $(DEBUG_DIR)/, $(OBJS))
# Output directories
RELEASE_ODIR := $(addprefix $(RELEASE_DIR)/, $(ODIR))
DEBUG_ODIR := $(addprefix $(DEBUG_DIR)/, $(ODIR))
RELEASE_ODIRS := $(sort $(dir $(RELEASE_OBJS)))
DEBUG_ODIRS := $(sort $(dir $(DEBUG_OBJS)))
# Dependency directories
RELEASE_DDIR := $(addprefix $(RELEASE_DIR)/, $(DDIR))
DEBUG_DDIR := $(addprefix $(DEBUG_DIR)/, $(DDIR))
RELEASE_DDIRS := $(sort $(dir $(RELEASE_DEPS)))
DEBUG_DDIRS := $(sort $(dir $(DEBUG_DEPS)))
# Flags for compiler
REL_CFLAGS := $(COMMON_FLAGS) $(RELEASE_FLAGS) $(RELEASE_DEFS)
DEB_CFLAGS := $(COMMON_FLAGS) $(DEBUG_FLAGS) $(DEBUG_DEFS)
LINK_CFLAGS := $(LINK_FLAGS) $(LIBS)
# ----------------------------------------
# Formating macros
# ----------------------------------------
ifeq ($(PLATFORM),Windows)
BOLD :=
NORMAL :=
RED :=
GREEN :=
else
BOLD := \033[1m
NORMAL := \033[0m
RED := \033[0;31m
GREEN := \033[0;32m
endif
# ----------------------------------------
# Compilation and linking rules
# ----------------------------------------
all: release
release: $(RELEASE_EXEC)
$(RELEASE_EXEC): $(RELEASE_OBJS)
@ echo "${GREEN}Building binary ${BOLD}$@${GREEN} using dependencies ${BOLD}$^${NORMAL}"
$(CC) $(filter %.s %.o,$^) -o $@ $(LINK_CFLAGS)
@ $(TOUCH) $@
$(RELEASE_ODIR)/%.o: $(SDIR)/%.c Makefile
$(RELEASE_ODIR)/%.o: $(SDIR)/%.c $(RELEASE_DDIR)/%.d Makefile | $(RELEASE_DDIRS) $(RELEASE_ODIRS)
@ echo "${GREEN}Building target ${BOLD}$@${GREEN}, using dependencies ${BOLD}$^${NORMAL}"
$(CC) $(REL_CFLAGS) -MT $@ -MMD -MP -MF $(patsubst %,%.Td,$(basename $(subst $(RELEASE_ODIR),$(RELEASE_DDIR),$@))) -c $(filter %.c %.s %.o,$^) -o $@
@ $(MOVE) $(patsubst %,%.Td,$(basename $(subst $(RELEASE_ODIR),$(RELEASE_DDIR),$@))) $(patsubst %,%.d,$(basename $(subst $(RELEASE_ODIR),$(RELEASE_DDIR),$@)))
@ $(TOUCH) $@
debug: $(DEBUG_EXEC)
$(DEBUG_EXEC): $(DEBUG_OBJS)
@ echo "${GREEN}Building binary ${BOLD}$@${GREEN} using dependencies ${BOLD}$^${NORMAL}"
$(CC) $(filter %.s %.o,$^) -o $@ $(LINK_CFLAGS)
@ $(TOUCH) $@
$(DEBUG_ODIR)/%.o: $(SDIR)/%.c Makefile
$(DEBUG_ODIR)/%.o: $(SDIR)/%.c $(DEBUG_DDIR)/%.d Makefile | $(DEBUG_DDIRS) $(DEBUG_ODIRS)
@ echo "${GREEN}Building target ${BOLD}$@${GREEN}, using dependencies ${BOLD}$^${NORMAL}"
$(CC) $(DEB_CFLAGS) -MT $@ -MMD -MP -MF $(patsubst %,%.Td,$(basename $(subst $(DEBUG_ODIR),$(DEBUG_DDIR),$@))) -c $(filter %.c %.s %.o,$^) -o $@
@ $(MOVE) $(patsubst %,%.Td,$(basename $(subst $(DEBUG_ODIR),$(DEBUG_DDIR),$@))) $(patsubst %,%.d,$(basename $(subst $(DEBUG_ODIR),$(DEBUG_DDIR),$@)))
@ $(TOUCH) $@
# ----------------------------------------
# Automatic dependency generation rules
# ----------------------------------------
# Based on http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
# https://gist.github.com/maxtruxa/4b3929e118914ccef057f8a05c614b0f
# https://spin.atomicobject.com/2016/08/26/makefile-c-projects/
$(RELEASE_DEPS): ;
.PRECIOUS: $(RELEASE_DEPS)
-include $(RELEASE_DEPS)
$(DEBUG_DEPS): ;
.PRECIOUS: $(DEBUG_DEPS)
-include $(DEBUG_DEPS)
# ----------------------------------------
# Script rules
# ----------------------------------------
$(RELEASE_ODIRS) $(DEBUG_ODIRS) $(RELEASE_DDIRS) $(DEBUG_DDIRS):
@ echo "${GREEN}Creating directory ${BOLD}$@${NORMAL}"
$(MKDIR_P) $@
run: release
@ echo "${GREEN}Running the aplication:${NORMAL}"
$(RELEASE_EXEC)
memcheck: release
valgrind --tool=memcheck --track-origins=yes --leak-check=full ./$(RELEASE_EXEC)
debugger: debug
@ echo "${GREEN}Running the aplication with the debugger${NORMAL}"
$(DEBUGGER) $(DEBUG_EXEC)
log:
@ echo "${GREEN}Git project log:${NORMAL}"
git log --oneline --decorate --all --graph
clean:
$(RM) -r $(RELEASE_DIR) $(DEBUG_DIR) *.d *.o *.a *.so *.exe
remade: clean release
.PHONY: all release debug run memcheck debugger log clean remade
# ----------------------------------------