-
Notifications
You must be signed in to change notification settings - Fork 2
/
Makefile
541 lines (413 loc) · 17.7 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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
define WELCOME_MESSAGE
default.inc not found, generating now
-----------------------------------------------------
| |
| Magic C/C++ Makefile |
| - Eric Lunderberg |
| |
-----------------------------------------------------
This is a makefile intended for compiling any
C/C++ project. It will find all source files,
compile them appropriately into executables and
libraries, and track all dependencies.
The makefile itself should not need to be
modified. A "default.inc" file has been generated,
which contains many options for customizing the
behavior of the makefile for your particular
project. The initial behavior assumes that most source
files are located in src, include files are
located in include, and source files containing
"int main()" are in the main directory.
Additional description of the behavior of the
makefile can be found in "default.inc"
endef #WELCOME_MESSAGE
define DEFAULT_INC_CONTENTS
# The C compiler to be used
CC = gcc
# The C++ compiler to be used
CXX = g++
# The archiver to be used
AR = ar
# The command to remove files
RM = rm -f
# Flags to be passed to both C and C++ code
CPPFLAGS = -Wall -Wextra -pedantic
# Flags to be passed to C code
CFLAGS =
# Flags to be passed to C++ code
CXXFLAGS = -g -O3
# Flags to be passed to the linker, prior to listing of object files.
LDFLAGS =
# Flags to be passed to the linker, after the listing of object files.
LDLIBS =
# A list of directories containing source files containing "int
# main()". Each file will be compiled into a separate executable.
EXE_DIRECTORIES = .
# A list of directories containing other source files. Each file will
# be compiled, with the resulting source file being linked into each
# executable.
SRC_DIRECTORIES = src
# A list of directories containing include files. Each directory will
# be made available for #include directives for included files.
INC_DIRECTORIES = include
# A list of directories that contain libraries. The list can also
# contain patterns that expand to directories that contain libraries.
# Each library is ex
LIB_DIRECTORIES = lib?*
# If BUILD_SHARED is non-zero, shared libraries will be generated. If
# BUILD_SHARED is greater than BUILD_STATIC, executables will be
# linked against the shared libraries.
BUILD_SHARED = 1
# If BUILD_STATIC is non-zero, static libraries will be generated. If
# BUILD_STATIC is greater than BUILD_SHARED, executables will be
# linked against the static libraries.
BUILD_STATIC = 0
# Mandatory arguments to both C and C++ compilers. These arguments
# will be passed even if CPPFLAGS has been overridden by command-line
# arguments.
CPPFLAGS_EXTRA =
# Mandatory arguments to the C compiler. These arguments will be
# passed even if CFLAGS has been overriden by command-line arguments.
CFLAGS_EXTRA =
# Mandatory arguments to the C++ compiler. These arguments will be
# passed even if CXXFLAGS has been overridden by command-line arguments.
CXXFLAGS_EXTRA = -std=c++17
# Mandatory arguments to the linker, before the listing of object
# files. These arguments will be passed even if LDFLAGS has been
# overridden by command-line arguments.
LDFLAGS_EXTRA = -Llib -Wl,-rpath,\$$ORIGIN/../lib
# Mandatory arguments to the linker, after the listing of object
# files. These arguments will be passed even if LDLIBS has been
# overridden by command-line arguments.
LDLIBS_EXTRA =
# Static libraries that should be linked into the executables. The
# order of libraries is the order of inclusion.
EXTERNAL_STATIC_LIBS =
# Flag to generate position-independent code. This is passed to
# object files being compiled to shared libraries, but not to any
# other object files.
PIC_FLAG = -fPIC
# A space-delimited list of file extensions to be compiled as C code.
# No element of this list should be present in CPP_EXT.
C_EXT = c
# A space-delimited list of file extensions to be compiled as C++
# code. No element of this list should be present in C_EXT.
CPP_EXT = C cc cpp cxx c++ cp
# A space-delimited list of file patterns to be excluded
# For example, this may contain a source file or directory
# that is not to be compiled in the current build.
# % can be used to create wildcard exclusions.
# Note: Files in the base directory are excluded as ./filename.
EXCLUSIONS =
# A function that, when given the name of a library, should return the
# output file of a shared library. For example, the default version,
# when passed "MyLibrary" as $(1), will return "lib/libMyLibrary.so".
SHARED_LIBRARY_NAME = lib/lib$(1).so
# A function that, when given the name of a library, should return the
# output file of a static library. For example, the default version,
# when passed "MyLibrary" as $(1), will return "lib/libMyLibrary.a".
STATIC_LIBRARY_NAME = lib/lib$(1).a
# A macro to determine whether executables will be linked against
# static libraries or shared libraries. By default, will compile
# against the shared libraries if BUILD_SHARED has a greater numeric
# value than BUILD_STATIC, and will compile against the static
# libraries otherwise.
# To always link against shared libraries, change this variable to
# 0. To always link against static libraries, change this variable to 1.
LINK_AGAINST_STATIC = $(shell test "$(BUILD_SHARED)" -gt "$(BUILD_STATIC)"; echo $$?)
# A function that, given the base name of a source file, returns the
# output filename of the executable. For example, the default
# version, when passed "MyProgram" as $(1), will return "bin/MyProgram".
EXE_NAME = bin/$(1)
# Determines whether the output is in color or not. To disable
# coloring, set this variable to 0.
USE_COLOR = 1
# The location to which extra resources should be installed.
INSTALL_DEST =
# Extra resources that should be copied to $(INSTALL_DEST). These can
# be either files or directories.
INSTALL_RESOURCES =
# A listing of the files and directories to be cleaned when running
# "make clean".
CLEAN_TARGETS = bin lib build
# Which system is the target system. This may be used by library
# targets to choose which system libraries to include.
SYSTEM = native
# The command to be run to run tests. This command will be run when
# running "make test". If this variable is an empty string, then this
# target will be left undefined.
TEST_COMMAND =
endef # DEFAULT_INC_CONTENTS
# Needed to replace newline with \n prior to printing.
define newline
endef
printf_quoted_percent=%%
printf_quoted_quote='"'"'
# " # Emacs isn't perfect in syntax highlighting
printf_escape = '$(subst %,$(printf_quoted_percent),$(subst ',$(printf_quoted_quote),$(subst $(newline),\n,$(value $1))))'
# ' # Emacs isn't perfect in syntax highlighting
# Eval DEFAULT_INC_CONTENTS first. This ensures that all required
# variables are defined, even if the default.inc present is from an
# older version of the makefile. This uses $(value ...) to avoid the
# first expansion of variables.
$(eval $(value DEFAULT_INC_CONTENTS))
# If default.inc does not exist, create it and display the welcome
# message.
ifeq (,$(wildcard default.inc))
$(shell printf $(call printf_escape,DEFAULT_INC_CONTENTS) > default.inc)
$(error $(WELCOME_MESSAGE))
endif
# Include the configuration file.
BUILD = default
include default.inc
# If the BUILD variable has been defined from the command line,
# include the appropriate build-target file.
ifneq ($(BUILD),default)
include build-targets/$(BUILD).inc
endif
ifeq ($(SYSTEM),native)
SYSTEM = $(shell uname | tr A-Z a-z)
endif
# Merge the mandatory and the optional flags.
ALL_CPPFLAGS := $(CPPFLAGS) $(CPPFLAGS_EXTRA)
ALL_CXXFLAGS := $(CXXFLAGS) $(CXXFLAGS_EXTRA)
ALL_CFLAGS := $(CFLAGS) $(CFLAGS_EXTRA)
ALL_LDFLAGS := $(LDFLAGS) $(LDFLAGS_EXTRA)
ALL_LDLIBS := $(LDLIBS) $(LDLIBS_EXTRA)
ALL_CPPFLAGS += $(addprefix -I,$(INC_DIRECTORIES))
.SECONDARY:
.PHONY: all clean force install_resources executables libraries
# Define all the ANSI color codes I want as options. If the USE_COLOR
# variable is zero, then don't define any of the codes.
ifneq ($(USE_COLOR),0)
RESET_COLOR = \033[m
BLUE = \033[1;34m
YELLOW = \033[1;33m
GREEN = \033[1;32m
RED = \033[1;31m
BLACK = \033[1;30m
MAGENTA = \033[1;35m
CYAN = \033[1;36m
WHITE = \033[1;37m
DBLUE = \033[0;34m
DYELLOW = \033[0;33m
DGREEN = \033[0;32m
DRED = \033[0;31m
DBLACK = \033[0;30m
DMAGENTA = \033[0;35m
DCYAN = \033[0;36m
DWHITE = \033[0;37m
BG_WHITE = \033[47m
BG_RED = \033[41m
BG_GREEN = \033[42m
BG_YELLOW = \033[43m
BG_BLUE = \033[44m
BG_MAGENTA = \033[45m
BG_CYAN = \033[46m
endif
# Define the colors to be used in run_and_test
COM_COLOR = $(DBLUE)
OBJ_COLOR = $(DCYAN)
OK_COLOR = $(DGREEN)
ERROR_COLOR = $(DRED)
WARN_COLOR = $(DYELLOW)
NO_COLOR = $(RESET_COLOR)
OK_STRING = "[OK]"
ERROR_STRING = "[ERROR]"
WARN_STRING = "[WARNING]"
# A macro that will be used repeatedly. Performs the command given,
# with colored output. Uses the colors as defined above.
ifdef VERBOSE
define run_and_test
echo "$(1)"
mkdir -p $(@D)
$(1)
endef
else
define run_and_test
mkdir -p $(@D)
printf "%b" "$(COM_COLOR)$(2) $(OBJ_COLOR)$(@F)$(NO_COLOR)\r"; \
$(1) 2> $@.log; \
RESULT=$$?; \
printf "%b" "$(COM_COLOR)$(2) $(OBJ_COLOR)"; \
if [ $$RESULT -ne 0 ]; then \
printf "%-40b%b" "$@" "$(ERROR_COLOR)$(ERROR_STRING)$(NO_COLOR)\n"; \
elif [ -s $@.log ]; then \
printf "%-40b%b" "$@" "$(WARN_COLOR)$(WARN_STRING)$(NO_COLOR)\n"; \
else \
printf "%-40b%b" "$(@F)" "$(OK_COLOR)$(OK_STRING)$(NO_COLOR)\n"; \
fi; \
cat $@.log; \
rm -f $@.log; \
exit $$RESULT
endef
endif
find_in_dir = $(foreach ext,$(2),$(wildcard $(1)/*.$(ext)))
find_in_dirs = $(foreach dir,$(1),$(call find_in_dir,$(dir),$(2)))
o_file_name = $(foreach file,$(1),build/$(BUILD)/build/$(basename $(file)).o)
# Find the source files that will be used.
SRC_FILES := $(call find_in_dirs,$(SRC_DIRECTORIES),$(CPP_EXT) $(C_EXT))
SRC_FILES := $(filter-out $(EXCLUSIONS),$(SRC_FILES))
O_FILES = $(call o_file_name,$(SRC_FILES))
# Find each library to be made.
LIBRARY_FOLDERS = $(foreach lib,$(LIB_DIRECTORIES),$(wildcard $(lib)))
library_src_files = $(filter-out $(addprefix $(1)/,$(LIBRARY_EXCLUSIONS)),\
$(call find_in_dirs,$(addprefix $(1)/,$(2)),$(CPP_EXT) $(C_EXT)))
library_o_files = $(call o_file_name,$(call library_src_files,$(1),$(2)))
library_os_files = $(addsuffix s,$(call library_o_files,$(1),$(2)))
all: default.inc executables libraries install_resources
@printf "%b" "$(DGREEN)Compilation successful$(NO_COLOR)\n"
ifneq ($(TEST_COMMAND),)
check: all
@echo "Running tests"
@$(TEST_COMMAND)
endif
# Update dependencies with each compilation
ALL_CPPFLAGS += -MMD -MP
-include $(shell find build -name "*.d" 2> /dev/null)
.build-target: force
@echo $(BUILD) | cmp -s - $@ || echo $(BUILD) > $@
define SAMPLE_MAKEFILE_INC_CONTENTS
# This file can be placed inside of a library directory to customize
# the behavior of that library. Each option, if left commented, will
# assumed its default value.
# The name of the library.
# Defaults to LIBNAME, where libLIBNAME is the directory.
LIBRARY_NAME = $(patsubst lib%,%,$(notdir $(CURDIR)))
# The flag that will be passed to the include the library in
# executables.
LIBRARY_FLAG = -l$(LIBRARY_NAME)
# The filename of the static library to be generated.
# By default, uses the pattern registered in the top-level default.inc
# This shouldn't have any slashes in it.
STATIC_LIBRARY_FILENAME = $(call STATIC_LIBRARY_NAME,$(LIBRARY_NAME))
# The filename of the shared library to be generated.
# By default, uses the pattern registered in the top-level default.inc
# This shouldn't have any slashes in it.
SHARED_LIBRARY_FILENAME = $(call SHARED_LIBRARY_NAME,$(LIBRARY_NAME))
# The directories containing source files for the library.
LIBRARY_SRC_DIRS = src
# The directories containing include files for the library. These
# directories will be added to the include path for all files in the
# project.
LIBRARY_INCLUDE_DIRS = include
# The directories containing include files for the library. These
# directories will be added to the include path only for files within
# this library
LIBRARY_PRIVATE_INCLUDE_DIRS =
# A space-delimited list of file patterns to be excluded
# For example, this may contain a source file or directory
# that is not to be compiled in the current build.
# % can be used to create wildcard exclusions.
# Note: Files in the library's directory should be listed as "filename",
# not "./filename".
LIBRARY_EXCLUSIONS =
# Extra flags that should be present when linking the shared library.
# This may include other libraries that should be included.
$(LIBRARY): SHARED_LDLIBS +=
# Compiler flag overrides for src files within this library.
$(LIBRARY):
endef
LibMakefile.inc:
@printf $(call printf_escape,SAMPLE_MAKEFILE_INC_CONTENTS) > Makefile.inc
@echo "Constructed Makefile.inc. Place this into a library directory to customize behavior"
# Rules to copy each library into their final location.
$(call SHARED_LIBRARY_NAME,%): build/$(BUILD)/$(call SHARED_LIBRARY_NAME,%) .build-target
@$(call run_and_test,rm -f $@ && cp $< $@,Copying )
$(call STATIC_LIBRARY_NAME,%): build/$(BUILD)/$(call STATIC_LIBRARY_NAME,%) .build-target
@$(call run_and_test,cp -f $< $@,Copying )
libraries:
STATIC_LIBRARY_OUTPUT :=
SHARED_LIBRARY_OUTPUT :=
define library_commands
CURDIR = $(1)
$$(eval $$(value SAMPLE_MAKEFILE_INC_CONTENTS))
LIBRARY = $$(SHARED_LIBRARY_FILENAME) $$(STATIC_LIBRARY_FILENAME)
-include $(1)/Makefile.inc
ifneq ($$(BUILD_STATIC),0)
STATIC_LIBRARY_OUTPUT += $$(STATIC_LIBRARY_FILENAME)
libraries: $$(STATIC_LIBRARY_FILENAME)
endif
ifneq ($$(BUILD_SHARED),0)
SHARED_LIBRARY_OUTPUT += $$(SHARED_LIBRARY_FILENAME)
libraries: $$(SHARED_LIBRARY_FILENAME)
endif
$$(LIBRARY): ALL_CPPFLAGS += $$(addprefix -I$(1)/,$$(LIBRARY_PRIVATE_INCLUDE_DIRS))
ALL_CPPFLAGS += $$(addprefix -I$(1)/,$$(LIBRARY_INCLUDE_DIRS))
ifneq ($$(LINK_AGAINST_STATIC),1)
ALL_LDLIBS += $$(LIBRARY_FLAG)
endif
# Build the libraries themselves
build/$$(BUILD)/$$(SHARED_LIBRARY_FILENAME): \
$$(call library_os_files,$(1),$$(LIBRARY_SRC_DIRS))
@$$(call run_and_test,$$(CXX) $$(ALL_LDFLAGS) $$^ -shared $$(SHARED_LDLIBS) -o $$@,Linking )
build/$$(BUILD)/$$(STATIC_LIBRARY_FILENAME): \
$$(call library_o_files,$(1),$$(LIBRARY_SRC_DIRS))
@$$(call run_and_test,$$(AR) rcs $$@ $$^,Linking )
# Copy the libraries into their final location
$$(SHARED_LIBRARY_FILENAME): build/$$(BUILD)/$$(SHARED_LIBRARY_FILENAME) .build-target
@$$(call run_and_test,rm -f $$@ && cp $$< $$@,Copying )
$$(STATIC_LIBRARY_FILENAME): build/$$(BUILD)/$$(STATIC_LIBRARY_FILENAME) .build-target
@$$(call run_and_test,cp -f $$< $$@,Copying )
endef
$(foreach lib,$(LIBRARY_FOLDERS),$(eval $(call library_commands,$(lib))))
# Rules to build each executable
$(call EXE_NAME,%): build/$(BUILD)/$(call EXE_NAME,%) .build-target
@$(call run_and_test,cp -f $< $@,Copying )
executables:
define exe_rules
EXE_SRC_FILES :=
EXE_INCLUDE_DIRS :=
EXTRA_SRC_FILES :=
CURDIR := $(1)
-include $(1)/Makefile.inc
ifeq ($$(EXE_SRC_FILES),)
EXE_SRC_FILES := $$(call find_in_dir,$(1),$$(CPP_EXT) $$(C_EXT))
EXE_SRC_FILES := $$(filter-out $$(EXCLUSIONS),$$(EXE_SRC_FILES))
else
EXE_SRC_FILES := $$(addprefix $$(CURDIR)/,$$(EXE_SRC_FILES))
endif
EXTRA_SRC_FILES := $$(addprefix $$(CURDIR)/,$$(EXTRA_SRC_FILES))
EXE_INCLUDE_DIRS := $$(addprefix $$(CURDIR)/,$$(EXE_INCLUDE_DIRS))
EXTRA_O_FILES := $$(call o_file_name,$$(EXTRA_SRC_FILES))
EXECUTABLES := $$(foreach cc,$$(EXE_SRC_FILES),$$(call EXE_NAME,$$(basename $$(notdir $$(cc)))))
ifeq ($$(LINK_AGAINST_STATIC),0)
build/$$(BUILD)/$$(call EXE_NAME,%): build/$$(BUILD)/build/$(1)/%.o $$(O_FILES) $$(EXTRA_O_FILES) $(EXTERNAL_STATIC_LIBS) | $$(SHARED_LIBRARY_OUTPUT)
@$$(call run_and_test,$$(CXX) $$(ALL_LDFLAGS) $$^ $$(ALL_LDLIBS) -o $$@,Linking )
else
build/$$(BUILD)/$$(call EXE_NAME,%): build/$$(BUILD)/build/$(1)/%.o $$(O_FILES) $$(EXTRA_O_FILES) $(EXTERNAL_STATIC_LIBS) $$(STATIC_LIBRARY_OUTPUT)
@$$(call run_and_test,$$(CXX) $$(ALL_LDFLAGS) $$^ $$(ALL_LDLIBS) -o $$@,Linking )
endif
executables: $$(EXECUTABLES)
$$(EXECUTABLES): ALL_CPPFLAGS += $$(addprefix -I,$$(EXE_INCLUDE_DIRS))
endef
$(foreach dir,$(EXE_DIRECTORIES),$(eval $(call exe_rules,$(dir))))
# Rules to build object files from C code
define C_BUILD_RULES
build/$$(BUILD)/build/%.o: %.$(1)
@$$(call run_and_test,$$(CC) -c $$(ALL_CPPFLAGS) $$(ALL_CFLAGS) $$< -o $$@,Compiling)
build/$$(BUILD)/build/%.os: %.$(1)
@$$(call run_and_test,$$(CC) -c $$(PIC_FLAG) $$(ALL_CPPFLAGS) $$(ALL_CFLAGS) $$< -o $$@,Compiling)
endef
$(foreach ext,$(C_EXT),$(eval $(call C_BUILD_RULES,$(ext))))
# Rules to build object files from C++ code
define CPP_BUILD_RULES
build/$$(BUILD)/build/%.o: %.$(1)
@$$(call run_and_test,$$(CXX) -c $$(ALL_CPPFLAGS) $$(ALL_CXXFLAGS) $$< -o $$@,Compiling)
build/$$(BUILD)/build/%.os: %.$(1)
@$$(call run_and_test,$$(CXX) -c $$(PIC_FLAG) $$(ALL_CPPFLAGS) $$(ALL_CXXFLAGS) $$< -o $$@,Compiling)
endef
$(foreach ext,$(CPP_EXT),$(eval $(call CPP_BUILD_RULES,$(ext))))
# Rules to install
install_resources:
define INSTALL_RULES
$$(INSTALL_DEST)/%: $(dir $(abspath $(1)))/%
@$$(call run_and_test,cp $$< $$@,Copying )
INSTALL_SOURCES = $$(shell find $(abspath $(1)) -type f)
install_resources: $$(patsubst $(dir $(abspath $(1)))%,$$(INSTALL_DEST)/%,$$(INSTALL_SOURCES))
endef
$(foreach source,$(INSTALL_RESOURCES),$(eval $(call INSTALL_RULES,$(source))))
# Cleanup
clean:
@printf "%b" "$(DYELLOW)Cleaning$(NO_COLOR)\n"
@$(RM) -r $(CLEAN_TARGETS) .build-target