-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
381 lines (335 loc) · 19.7 KB
/
Makefile
File metadata and controls
381 lines (335 loc) · 19.7 KB
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
# ================================ AUTODETECT TOOLS =============================== #
# Try to find the newest clang-tidy from version 21 down to 15
# If none are found in that range, default to clang-tidy-20 (original value)
DETECTED_CLANG_TIDY := $(firstword $(foreach v,$(shell seq 21 -1 15),$(if $(shell command -v clang-tidy-$(v) >/dev/null 2>&1 && echo clang-tidy-$(v)),clang-tidy-$(v))))
ifeq ($(DETECTED_CLANG_TIDY),)
FOUND_CLANG_TIDY = clang-tidy-20
else
FOUND_CLANG_TIDY = $(DETECTED_CLANG_TIDY)
endif
$(info Using clang-tidy: $(FOUND_CLANG_TIDY))
# Try to find the newest scan-build from version 21 down to 15
# If none are found in that range, default to scan-build-20 (original value)
DETECTED_SCAN_BUILD := $(firstword $(foreach v,$(shell seq 21 -1 15),$(if $(shell command -v scan-build-$(v) >/dev/null 2>&1 && echo scan-build-$(v)),scan-build-$(v))))
ifeq ($(DETECTED_SCAN_BUILD),)
FOUND_SCAN_BUILD = scan-build-20
else
FOUND_SCAN_BUILD = $(DETECTED_SCAN_BUILD)
endif
$(info Using scan-build: $(FOUND_SCAN_BUILD))
# ================================ VARIABLES ================================= #
NAME = ircserv
CXX = c++
CXXFLAGS = -Wall -Wextra -Werror -std=c++98 -g3
INCLUDES = -Iincludes
# Source files
SRCS = src/main.cpp src/IRCServer.cpp src/Commands.cpp src/EssentialCommands.cpp \
src/OperatorCommands.cpp src/AdvancedCommands.cpp src/Channel.cpp \
src/Validation.cpp src/Security.cpp src/Networking.cpp src/CommandLineParser.cpp \
src/IRCServerHelpers.cpp
OBJS = $(SRCS:.cpp=.o)
# Plist files for static analysis
PLIST = AdvancedCommands.plist Commands.plist EssentialCommands.plist IRCServer.plist \
main.plist OperatorCommands.plist Utils.plist
# Test scripts
TESTERDIR = testers
TESTERS = c4_thinking.sh comprehensive_test.sh enhanced_test.sh extended_unit_tests.sh \
ft_irc_compliance_test.sh quick_test.sh r1.sh rfc_compliance_extended.sh \
stress_test.sh test_irc.sh
# ========================= STATIC ANALYSIS TOOLS ============================ #
# Clang-tidy configuration - Minimal, crucial checks only for C++98
CLANG_TIDY = $(FOUND_CLANG_TIDY)
# ─── Exclusions (everything else except C++98‐safe bits) ───────────────────────
TIDY_CHECKS := '*,\
-hicpp-use-nullptr,\
-readability-identifier-length,\
-llvmlibc-restrict-system-libc-headers,\
-modernize-use-nullptr,\
-llvm-header-guard,\
-cppcoreguidelines-pro-bounds-pointer-arithmetic,\
-hicpp-use-auto,\
-modernize-use-auto,\
-modernize-loop-convert,\
-llvmlibc-implementation-in-namespace,\
-concurrency-mt-unsafe,\
-fuchsia-default-arguments-calls,\
-hicpp-use-equals-delete,\
-modernize-use-equals-delete,\
-altera-unroll-loops,\
-fuchsia-default-arguments-declarations,\
-cppcoreguidelines-pro-type-vararg,\
-misc-include-cleaner,\
-altera-id-dependent-backward-branch,\
-hicpp-vararg,\
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,\
-hicpp-no-array-decay,\
-cppcoreguidelines-avoid-non-const-global-variables,\
-llvmlibc-callee-namespace,\
-android-cloexec-accept,\
-cppcoreguidelines-pro-type-const-cast,\
-cppcoreguidelines-pro-bounds-constant-array-index,\
-altera-struct-pack-align,\
-readability-function-cognitive-complexity'
# ─── Explicit “const‐bug” / static‐analysis list ─────────────────────────────────
TIDY_CONST_BUG_CHECKS := 'clang-analyzer-*,\
bugprone-*,\
performance-*,\
misc-const-correctness,\
misc-misplaced-const,\
cppcoreguidelines-avoid-const-or-ref-data-members,\
readability-avoid-const-params-in-decls,\
readability-const-return-type,\
readability-make-member-function-const,\
llvm-include-order,\
cppcoreguidelines-init-variables,\
llvmlibc-inline-function-decl,\
hicpp-braces-around-statements,\
readability-braces-around-statements,\
google-runtime-int,\
readability-implicit-bool-conversion,\
readability-isolate-declaration,\
readability-redundant-string-init,\
cppcoreguidelines-special-member-functions,\
hicpp-special-member-functions,\
readability-convert-member-functions-to-static,\
google-explicit-constructor,\
hicpp-explicit-conversions,\
hicpp-signed-bitwise,\
hicpp-deprecated-headers,\
modernize-deprecated-headers,\
misc-use-anonymous-namespace,\
misc-definitions-in-headers'
# Extra arguments for C++98 compatibility
TIDY_EXTRA_ARGS = --extra-arg=-std=c++98 \
--extra-arg=-Iincludes \
--extra-arg=-Wno-c++98-compat
# Clang-format configuration
CLANG_FORMAT = clang-format-20
FORMAT_FLAGS = -i -style=google
# =============================== BUILD RULES ================================ #
# Default target
all: $(NAME)
# Main build target
$(NAME): $(OBJS)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(NAME) $(OBJS)
@echo "✅ Build complete: $(NAME)"
# Pattern rule for object files
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
# ============================= BUILD VARIANTS =============================== #
# Debug build with sanitizers and debug symbols
debug: CXXFLAGS += -g3 -O3 -fno-optimize-sibling-calls -fno-omit-frame-pointer
debug: CXXFLAGS += -fsanitize=address,undefined
debug: re
mv $(NAME) ircserv_debug
@echo "✅ Debug build complete with sanitizers"
# Release build with optimizations
release: CXXFLAGS += -O3 -DNDEBUG
release: re
@echo "✅ Release build complete"
# Build with integrated clang-tidy checks (slower but more thorough)
tidy-build: clean
@echo "🔍 Building with integrated clang-tidy checks..."
@for src in $(SRCS); do \
echo "Analyzing $$src..."; \
$(CLANG_TIDY) $$src --checks=$(TIDY_CHECKS) \
$(TIDY_EXTRA_ARGS) \
--warnings-as-errors='' --quiet -- $(CXXFLAGS) $(INCLUDES); \
if [ $$? -ne 0 ]; then exit 1; fi; \
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $$src -o $${src%.cpp}.o; \
done
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(NAME) $(OBJS)
@echo "✅ Tidy build complete"
# ========================== CLEANING TARGETS ================================ #
clean:
rm -f $(OBJS) $(PLIST) build.log build_check.log
rm -f compile_commands.json
@echo "🧹 Cleaned object files"
fclean: clean
rm -f $(NAME) ircserv_debug
@echo "🧹 Full clean complete"
re: fclean all
# ======================== STATIC ANALYSIS TARGETS =========================== #
# Run clang-tidy on all source files
tidy:
@echo "🔍 Running clang-tidy with C++98 compatible checks..."
@$(CLANG_TIDY) $(SRCS) \
--checks=$(TIDY_CHECKS) \
$(TIDY_EXTRA_ARGS) \
--header-filter='includes/.*' \
--system-headers=false \
--warnings-as-errors='' \
--quiet \
-- $(CXXFLAGS) $(INCLUDES)
@echo "✅ clang-tidy analysis complete"
lint: tidy
# Quick clang-tidy check without building
tidy-check:
@echo "🔍 Quick clang-tidy check (no build)..."
@$(CLANG_TIDY) $(SRCS) --checks=$(TIDY_CHECKS) \
$(TIDY_EXTRA_ARGS) \
--warnings-as-errors='' --quiet -- $(CXXFLAGS) $(INCLUDES) # --warnings-as-errors='*' if erroring on warning is needed
# Check for const correctness and bugprone issues only
tidy-const-bug:
@echo "🔍 Checking const correctness and bugprone issues..."
@$(CLANG_TIDY) $(SRCS) \
--checks=$(TIDY_CONST_BUG_CHECKS) \
$(TIDY_EXTRA_ARGS) \
--header-filter='includes/.*' \
--system-headers=false \
--quiet \
-- $(CXXFLAGS) $(INCLUDES)
@echo "✅ Const correctness and bugprone check complete"
# Apply clang-tidy fixes automatically (C++98 safe)
tidy-fix:
@echo "🔧 Applying C++98-compatible clang-tidy fixes..."
@echo "⚠️ NOTE: Only applying safe fixes for C++98"
@for src in $(SRCS); do \
echo "Fixing $$src..."; \
$(CLANG_TIDY) $$src --fix --fix-errors --fix-notes --checks=$(TIDY_CHECKS) \
$(TIDY_EXTRA_ARGS) \
--quiet -- $(CXXFLAGS) $(INCLUDES) 2>&1 | \
grep -v "warning generated" || true; \
done
@echo "✅ Tidy fixes applied"
@echo "🔨 Rebuilding with fixed code..."
@$(MAKE) clean
@$(MAKE) all
# Apply all automatic fixes (format + tidy)
fix-all: format tidy-fix
@echo "✅ All automatic fixes applied"
# Generate compilation database for better tool integration
compile_commands.json:
@echo "📝 Generating compilation database..."
@echo "[" > compile_commands.json
@first=1; \
for src in $(SRCS); do \
if [ $$first -eq 0 ]; then echo "," >> compile_commands.json; fi; \
echo " {" >> compile_commands.json; \
echo " \"directory\": \"$$(pwd)\"," >> compile_commands.json; \
echo " \"command\": \"$(CXX) $(CXXFLAGS) $(INCLUDES) -c $$src\"," >> compile_commands.json; \
echo " \"file\": \"$$src\"" >> compile_commands.json; \
echo -n " }" >> compile_commands.json; \
first=0; \
done
@echo "" >> compile_commands.json
@echo "]" >> compile_commands.json
@echo "✅ Compilation database generated"
# GCC static analysis
fanalyzer: fclean
@echo "🔍 Running GCC static analyzer..."
@g++ -fanalyzer -Wall -Wextra -std=c++98 $(INCLUDES) $(SRCS) -o /dev/null 2>&1 | \
grep -v "stl_tree.h" | \
grep -v "CWE-457" | \
grep -E "(warning|error)" || echo "✅ GCC analyzer: No actionable issues found"
# Clang static analysis
analyzer: CXX = clang++
analyzer: CXXFLAGS += --analyze
analyzer: fclean
@echo "🔍 Running Clang static analyzer..."
$(CXX) $(CXXFLAGS) $(INCLUDES) $(SRCS)
@echo "✅ Clang analyzer complete"
# Build with all warnings enabled (Clang only)
weverything: CXX = clang++
weverything: CXXFLAGS += -Weverything -Wno-c++98-compat-pedantic -Wno-padded
weverything: re
@echo "✅ Build with -Weverything complete"
# Cppcheck static analysis
cppcheck:
@echo "🔍 Running cppcheck..."
@cppcheck --enable=all --inconclusive --std=c++98 --language=c++ \
--check-config --suppress=missingIncludeSystem \
--suppress=unmatchedSuppression --suppress=missingInclude \
--error-exitcode=1 $(INCLUDES) $(SRCS) 2>&1 | \
grep -v "information: Couldn't find path" || true
@echo "✅ Cppcheck analysis complete"
# Scan-build static analysis
scan-build: fclean
@echo "🔍 Running scan-build..."
@$(FOUND_SCAN_BUILD) --use-cc=clang --use-c++=clang++ \
-enable-checker alpha \
-enable-checker security -enable-checker unix -enable-checker core \
-enable-checker cplusplus -enable-checker deadcode -enable-checker nullability \
-analyzer-config aggressive-binary-operation-simplification=true \
--status-bugs -v -V make
@echo "✅ Scan-build analysis complete"
# Combined static analysis (runs all analyzers)
analyze-all: tidy cppcheck fanalyzer
@echo "✅ All static analysis complete"
# ========================= TESTING & VALIDATION ============================= #
# Shellcheck for test scripts
shellcheck:
@echo "🔍 Running shellcheck on test scripts..."
@cd $(TESTERDIR) && shellcheck --enable=all --extended-analysis=true $(TESTERS)
@echo "✅ Shellcheck complete"
# Valgrind memory check
valgrind: $(NAME)
@echo "🔍 Running valgrind memory check..."
@echo "Note: You'll need to run the server and test it manually"
@echo "Example: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./$(NAME) 6667 password"
# Code formatting
format:
@echo "🎨 Formatting code..."
@$(CLANG_FORMAT) $(FORMAT_FLAGS) $(SRCS) includes/*.hpp
@echo "✅ Code formatting complete"
# Check formatting without modifying files
format-check:
@echo "🔍 Checking code format..."
@$(CLANG_FORMAT) --dry-run --Werror $(SRCS) includes/*.hpp && \
echo "✅ Code format check passed" || \
(echo "❌ Code format check failed. Run 'make format' to fix." && exit 1)
# ============================== HELP TARGET ================================= #
help:
@echo ""
@printf "\033[38;5;53m┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[38;5;93m●\033[0m \033[38;5;91m●\033[0m \033[38;5;90m●\033[0m \033[1;38;5;231;48;5;237m ft_irc Makefile \033[0m \033[38;5;90m●\033[0m \033[38;5;91m●\033[0m \033[38;5;93m●\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;172m⚙️ BUILD TARGETS\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mall\033[0m │ Build the IRC server (default) \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mdebug\033[0m │ Build with debug symbols and sanitizers \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mrelease\033[0m │ Build with optimizations \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mtidy-build\033[0m │ Build with integrated clang-tidy checks \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mre\033[0m │ Clean and rebuild \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;172m🧹 CLEANING\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mclean\033[0m │ Remove object files and dependencies \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mfclean\033[0m │ Remove all build artifacts \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;172m🔍 STATIC ANALYSIS\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mtidy\033[0m │ Run clang-tidy with C++98 checks \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mtidy-check\033[0m │ Quick clang-tidy check (no build) \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mtidy-const-bug\033[0m │ Check const correctness and bugprone issues \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mtidy-fix\033[0m │ Apply C++98-safe clang-tidy fixes \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mfix-all\033[0m │ Apply all automatic fixes (format + tidy) \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[38;5;60m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mcppcheck\033[0m │ Run cppcheck static analysis \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mfanalyzer\033[0m │ Run GCC static analyzer \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104manalyzer\033[0m │ Run Clang static analyzer \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mscan-build\033[0m │ Run scan-build analysis \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mweverything\033[0m │ Build with -Weverything (Clang) \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104manalyze-all\033[0m │ Run all static analyzers \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;172m🧪 TESTING & VALIDATION\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mshellcheck\033[0m │ Check shell scripts for errors \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mvalgrind\033[0m │ Run memory leak checks with valgrind \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mformat\033[0m │ Format code with clang-format \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mformat-check\033[0m │ Check if code follows formatting standards \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;172m🛠️ UTILITIES\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mcompile_commands.json\033[0m │ Generate compilation database \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[1;38;5;104mhelp\033[0m │ Display this help message \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\033[0m\n"
@printf "\033[38;5;53m┃\033[0m \033[38;5;139mft_irc: IRC Server Implementation\033[0m \033[38;5;53m┃\033[0m\n"
@printf "\033[38;5;53m┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\033[0m\n"
@echo ""
# Mark targets that don't produce files
.PHONY: all clean fclean re debug release tidy-build tidy tidy-check tidy-const-bug \
tidy-fix fix-all compile_commands.json fanalyzer analyzer weverything \
cppcheck scan-build analyze-all shellcheck valgrind format format-check help
# Keep intermediate files
.PRECIOUS: %.o %.d