-
Notifications
You must be signed in to change notification settings - Fork 0
/
Makefile
178 lines (123 loc) · 4.62 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
NAME := image-transformer
##### Compiler / analyzer common configuration.
CC = clang
LINKER = $(CC)
RM = rm -rf
MKDIR = mkdir -p
# Clang-tidy
CLANG_TIDY = clang-tidy
_noop =
_space = $(noop) $(noop)
_comma = ,
# Using `+=` to let user define their own checks in command line
CLANG_TIDY_CHECKS += $(strip $(file < clang-tidy-checks.txt))
CLANG_TIDY_ARGS = \
-warnings-as-errors=* -header-filter="$(abspath $(INCDIR.main))/.*" \
-checks="$(subst $(_space),$(_comma),$(CLANG_TIDY_CHECKS))" \
# Sanitizers
CFLAGS.none :=
CFLAGS.asan := -fsanitize=address
CFLAGS.lsan := -fsanitize=leak
CFLAGS.msan := -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -fno-optimize-sibling-calls
CFLAGS.usan := -fsanitize=undefined
SANITIZER ?= none
ifeq ($(SANITIZER),)
override SANITIZER := none
endif
ifeq ($(words $(SANITIZER)),1)
ifeq ($(filter $(SANITIZER),all asan lsan msan usan none),)
$(error Please provide correct argument value for SANITIZER: all, asan, lsan, msan, usan or none)
endif
endif
# Using `+=` to let user define their own flags in command line
CFLAGS += $(CFLAGS.$(SANITIZER))
LDFLAGS += $(CFLAGS.$(SANITIZER))
ifeq ($(SANITIZER),none)
OBJDIR = obj
BUILDDIR = build
else
OBJDIR = obj/$(SANITIZER)
BUILDDIR = build/$(SANITIZER)
endif
##### Configuration for `main` target.
SOLUTION_DIR = solution
SRCDIR.main = $(SOLUTION_DIR)/src
INCDIR.main = $(SOLUTION_DIR)/include
OBJDIR.main = $(OBJDIR)/$(SOLUTION_DIR)
SOURCES.main += $(wildcard $(SRCDIR.main)/*.c) $(wildcard $(SRCDIR.main)/*/*.c)
TARGET.main := $(BUILDDIR)/$(NAME)
CFLAGS.main += $(strip $(file < $(SOLUTION_DIR)/compile_flags.txt)) $(CFLAGS) -I$(INCDIR.main)
##### Configuration for `tester` target.
TESTER_DIR = tester
TESTER_SCRIPT = $(TESTER_DIR)/tester.sh
SRCDIR.tester = $(TESTER_DIR)/src
INCDIR.tester = $(TESTER_DIR)/include
OBJDIR.tester = $(OBJDIR)/$(TESTER_DIR)
SOURCES.tester += $(wildcard $(SRCDIR.tester)/*.c)
TARGET.tester := $(BUILDDIR)/image-tester
CFLAGS.tester += $(strip $(file < $(TESTER_DIR)/compile_flags.txt)) $(CFLAGS) -I$(INCDIR.tester)
##### Rule templates. Should be instantiated with $(eval $(call template, ...))
# I use $$(var) in some variables to avoid variable expanding too early.
# I do not remember when $(var) is expanded in `define` rules, but $$(var)
# is expanded exactly at $(eval ...) call.
# Template for running submake on each SANITIZER value when SANITIZER=all is used.
# $(1) - SANITIZER value (none/asan/lsan/msan/usan)
define make-sanitizer-rule
GOALS.$(1) := $$(patsubst %,%.$(1),$$(MAKECMDGOALS))
$$(MAKECMDGOALS): $$(GOALS.$(1))
.PHONY: $$(GOALS.$(1))
$$(GOALS.$(1)):
@echo Running 'make $$(patsubst %.$(1),%,$$@)' with SANITIZER=$(1)
@$(MAKE) $$(patsubst %.$(1),%,$$@) SANITIZER=$(1)
endef
# Template for compilation and linking rules + depfile generation and inclusion
# $(1) - target name (main/tester)
define make-compilation-rule
OBJECTS.$(1) := $$(SOURCES.$(1):$$(SRCDIR.$(1))/%.c=$$(OBJDIR.$(1))/%.o)
SRCDEPS.$(1) := $$(OBJECTS.$(1):%.o=%.o.d)
DIRS.$(1) := $$(sort $$(dir $$(OBJECTS.$(1)) $$(TARGET.$(1))))
DIRS += $$(DIRS.$(1))
.PHONY: build-$(1)
build-$(1): $$(TARGET.$(1))
$$(TARGET.$(1)): $$(OBJECTS.$(1)) | $$(DIRS.$(1))
$(LINKER) $(LDFLAGS) $$(OBJECTS.$(1)) -o $$@
$$(OBJDIR.$(1))/%.o: $$(SRCDIR.$(1))/%.c | $$(DIRS.$(1))
$(CC) $(CFLAGS.$(1)) -c $$< -o $$@ -MD -MF [email protected] -MP
-include $$(SRCDEPS.$(1))
endef
# Template for testing rules.
# $(1) - directory with test
define make-test-rule
TST_NAME.$(1) := $$(notdir $(1))
TST_INPUT.$(1) := $(1)/input.bmp
TST_OUTPUT.$(1) := $(OBJDIR.tester)/$$(TST_NAME.$(1)).bmp
TST_EXPECTED.$(1) := $(1)/output_expected.bmp
TST_LOG_OUT.$(1) := $(OBJDIR.tester)/$$(TST_NAME.$(1))_out.log
TST_LOG_ERR.$(1) := $(OBJDIR.tester)/$$(TST_NAME.$(1))_err.log
.PHONY: $$(TST_OUTPUT.$(1)) test-$$(TST_NAME.$(1))
test: test-$$(TST_NAME.$(1))
test-$$(TST_NAME.$(1)): build-main build-tester
$(TESTER_SCRIPT) $$(TST_NAME.$(1)) \
--main-cmd '$(TARGET.main) $$(TST_INPUT.$(1)) $$(TST_OUTPUT.$(1))' \
--tester-cmd '$(TARGET.tester) $$(TST_OUTPUT.$(1)) $$(TST_EXPECTED.$(1))' \
--log-dir '$(OBJDIR.tester)'
endef
##### Rules and targets.
.PHONY: all test clean check
ifeq ($(MAKECMDGOALS),)
MAKECMDGOALS := all
endif
ifeq ($(SANITIZER),all)
# Do all the work in sub-makes
$(foreach sanitizer,none asan lsan msan usan,$(eval $(call make-sanitizer-rule,$(sanitizer))))
else
all: build-main
check:
$(CLANG_TIDY) $(CLANG_TIDY_ARGS) $(SOURCES.main)
$(foreach target,main tester,$(eval $(call make-compilation-rule,$(target))))
$(foreach test,$(sort $(wildcard $(TESTER_DIR)/tests/*)),$(eval $(call make-test-rule,$(test))))
clean:
$(RM) $(OBJDIR) $(BUILDDIR)
$(sort $(DIRS)):
$(MKDIR) $@
endif