1+ # General Variables
2+ SHELL := bash
3+
14# Build configuration
25CC ?= clang
36ENABLE_ASAN ?= 0
47BUILD_TYPE ?= debug
5- CFLAGS := -Wall -Wextra -pedantic -std=c11 -Iinclude
6- LDFLAGS :=
7- LIBS :=
88
99# Directories
10- BIN_DIR := bin
11- TEST_DIR := test
12- INC_DIR := include
13- DOC_DIR := doc
10+ BIN_DIR := bin
11+ TEST_DIR := test
12+ INC_DIR := include
13+ DOC_DIR := doc
14+ ASSET_DIR := assets
15+
16+ # Flags
17+ CFLAGS_BASE := -Wall -Wextra -pedantic -std=c11 -I$(INC_DIR )
18+ LDFLAGS :=
19+ LIBS :=
1420
1521# Sanitizer configuration
1622ifeq ($(ENABLE_ASAN ) ,1)
17- CFLAGS + = -fsanitize=address
18- LDFLAGS += -fsanitize=address
23+ CFLAGS_SAN : = -fsanitize=address
24+ LDFLAGS += -fsanitize=address
1925 export ASAN_OPTIONS=verbosity=0 :detect_leaks=1:log_path=asan.log
2026endif
2127
2228# Build type configuration
2329ifeq ($(BUILD_TYPE ) ,release)
24- CFLAGS + = -O2 -DNDEBUG
30+ CFLAGS_TYPE : = -O2 -DNDEBUG
2531else
26- CFLAGS + = -g -O0
32+ CFLAGS_TYPE : = -g -O0
2733endif
2834
29- # Test and benchmark binaries
30- TEST_BINARY := $(BIN_DIR ) /test_bptree
31- BENCH_BINARY := $(BIN_DIR ) /bench_bptree
35+ # Combine flags
36+ CFLAGS := $(CFLAGS_BASE ) $(CFLAGS_SAN ) $(CFLAGS_TYPE )
37+
38+ # Binary names
39+ TEST_BINARY := $(BIN_DIR ) /test_bptree
40+ BENCH_BINARY := $(BIN_DIR ) /bench_bptree
3241EXAMPLE_BINARY := $(BIN_DIR ) /example
3342
43+ # Default target
3444.DEFAULT_GOAL := help
3545
3646.PHONY : help
37- help : # # Show the targets and their descriptions
38- @grep -E ' ^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST ) | awk ' BEGIN {FS = ":.*?## "}; \
39- {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
47+ help : # # Show available targets
48+ @grep -E ' ^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST ) | \
49+ awk ' BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
4050
4151$(BIN_DIR ) :
4252 mkdir -p $(BIN_DIR )
4353
54+ # #############################################################################################################
55+ # # Pattern Rule for Building Binaries
56+ # #############################################################################################################
57+ # This rule compiles a C file in the TEST_DIR into a binary in BIN_DIR
58+ $(BIN_DIR ) /% : $(TEST_DIR ) /% .c | $(BIN_DIR )
59+ $(CC ) $(CFLAGS ) -o $@ $< $(LDFLAGS ) $(LIBS )
60+
61+ # #############################################################################################################
62+ # # Conventional Targets
63+ # #############################################################################################################
64+
4465.PHONY : all
45- all : clean doc test bench example # # Build all targets and run tests and benchmarks
66+ all : clean test bench example doc # # Build everything, run tests, benchmarks, and generate docs
4667
4768.PHONY : test
4869test : $(TEST_BINARY ) # # Build and run tests
4970 @echo " Running tests..."
5071 ./$(TEST_BINARY )
5172
52- $(TEST_BINARY ) : $(TEST_DIR ) /test_bptree.c $(INC_DIR ) /bptree.h | $(BIN_DIR )
53- $(CC ) $(CFLAGS ) -o $@ $< $(LDFLAGS ) $(LIBS )
54-
5573.PHONY : bench
5674bench : $(BENCH_BINARY ) # # Build and run benchmarks
5775 @echo " Running benchmarks..."
5876 ./$(BENCH_BINARY )
5977
60- $(BENCH_BINARY ) : $(TEST_DIR ) /bench_bptree.c $(INC_DIR ) /bptree.h | $(BIN_DIR )
61- $(CC ) $(CFLAGS ) -o $@ $< $(LDFLAGS ) $(LIBS )
78+ .PHONY : example
79+ example : $(EXAMPLE_BINARY ) # # Run example program
80+ @echo " Running the example..."
81+ ./$(EXAMPLE_BINARY )
6282
6383.PHONY : clean
6484clean : # # Remove build artifacts
@@ -68,7 +88,7 @@ clean: ## Remove build artifacts
6888.PHONY : format
6989format : # # Format source code
7090 @echo " Formatting source code..."
71- @command -v clang-format > /dev/null 2>&1 || { echo >&2 " clang-format not found." ; exit 1; }
91+ @command -v clang-format > /dev/null 2>&1 || { echo " clang-format not found." ; exit 1; }
7292 clang-format -i $(TEST_DIR ) /* .c $(INC_DIR ) /* .h
7393
7494.PHONY : lint
@@ -77,8 +97,8 @@ lint: ## Run linter checks
7797 cppcheck --enable=all --inconclusive --quiet --std=c11 -I$(INC_DIR ) $(TEST_DIR )
7898
7999.PHONY : install
80- install : # # Install header file
81- @echo " Installing bptree.h system-wide ..."
100+ install : # # Install header file system-wide
101+ @echo " Installing bptree.h..."
82102 install -d /usr/local/include
83103 install -m 0644 $(INC_DIR ) /bptree.h /usr/local/include/
84104
@@ -88,10 +108,11 @@ uninstall: ## Remove installed header file
88108 rm -f /usr/local/include/bptree.h
89109
90110.PHONY : install-deps
91- install-deps : # # Install development dependencies (for Debian-based systems )
111+ install-deps : # # Install development dependencies (Debian-based)
92112 @echo " Installing development dependencies..."
93113 sudo apt-get update && sudo apt-get install -y \
94- gcc gdb clang clang-format cppcheck valgrind
114+ gcc gdb clang clang-format clang-tools cppcheck valgrind graphviz \
115+ kcachegrind graphviz linux-tools-common linux-tools-generic linux-tools-$(shell uname -r)
95116
96117.PHONY : coverage
97118coverage : CFLAGS += -fprofile-arcs -ftest-coverage
@@ -103,33 +124,76 @@ coverage: clean $(TEST_BINARY) ## Generate code coverage report
103124 gcov -o $(BIN_DIR ) $(TEST_DIR ) /test_bptree.c
104125 @echo " Coverage report generated"
105126
106- .PHONY : memcheck
107- memcheck : $(EXAMPLE_BINARY ) $(TEST_BINARY ) $(BENCH_BINARY ) # # Run memory checks with Valgrind
108- @echo " Running memory checks..."
109- @echo " Running example with Valgrind..."
110- valgrind --leak-check=full --show-leak-kinds=all ./$(EXAMPLE_BINARY )
111- @echo " Running tests with Valgrind..."
112- valgrind --leak-check=full --show-leak-kinds=all ./$(TEST_BINARY )
113- @echo " Running benchmarks with Valgrind..."
114- valgrind --leak-check=full --show-leak-kinds=all ./$(BENCH_BINARY )
115- @echo " Valgrind checks completed"
116-
117127.PHONY : doc
118- doc : # # Generate documentation
128+ doc : # # Generate documentation using Doxygen
119129 @echo " Generating documentation..."
120130 @test -f Doxyfile || { echo " Error: Doxyfile not found." ; exit 1; }
121131 doxygen Doxyfile
122132
123- .PHONY : example
124- example : $( EXAMPLE_BINARY ) # # Run examples
125- @echo " Running the example ..."
126- ./ $( EXAMPLE_BINARY )
133+ .PHONY : figure
134+ figure : # # Generate figures using Graphviz
135+ @echo " Generating figures ..."
136+ @ $( SHELL ) $( ASSET_DIR ) /make_figures.sh $( ASSET_DIR )
127137
128- $(EXAMPLE_BINARY ) : $(TEST_DIR ) /example.c $(INC_DIR ) /bptree.h | $(BIN_DIR )
129- $(CC ) $(CFLAGS ) -o $@ $< $(LDFLAGS ) $(LIBS )
138+ # #############################################################################################################
139+ # # Release and Debugging Targets
140+ # #############################################################################################################
130141
131142.PHONY : release
132143release : BUILD_TYPE=release
133- release : all # # Build everything in release mode (optimized)
134- @echo " Building in release mode..."
135- @$(MAKE ) all
144+ release : all # # Like 'all', but builds with code optimizations
145+ @echo " Built in release mode."
146+
147+ .PHONY : memcheck
148+ memcheck : $(EXAMPLE_BINARY ) $(TEST_BINARY ) $(BENCH_BINARY ) # # Run Valgrind memory checks
149+ @echo " Running Valgrind memory checks..."
150+ valgrind --leak-check=full --show-leak-kinds=all ./$(EXAMPLE_BINARY )
151+ valgrind --leak-check=full --show-leak-kinds=all ./$(TEST_BINARY )
152+ valgrind --leak-check=full --show-leak-kinds=all ./$(BENCH_BINARY )
153+ @echo " Valgrind checks completed"
154+
155+ .PHONY : profile
156+ profile : CFLAGS += -pg
157+ profile : LDFLAGS += -pg
158+ profile : clean $(BENCH_BINARY ) # # Build and run with gprof profiling
159+ @echo " Running profiling (gprof)..."
160+ ./$(BENCH_BINARY )
161+ gprof $(BENCH_BINARY ) gmon.out > profile_report.txt
162+ @echo " Profile report saved as profile_report.txt"
163+
164+ .PHONY : ubsan
165+ ubsan : CFLAGS += -fsanitize=undefined
166+ ubsan : LDFLAGS += -fsanitize=undefined
167+ ubsan : clean $(TEST_BINARY ) # # Run tests with UndefinedBehaviorSanitizer
168+ @echo " Running UBSan tests..."
169+ UBSAN_OPTIONS=print_stacktrace=1 ./$(TEST_BINARY )
170+
171+ .PHONY : analyze
172+ analyze : # # Run Clang Static Analyzer
173+ @echo " Running Clang Static Analyzer..."
174+ scan-build --status-bugs $(MAKE ) clean all
175+
176+ .PHONY : perf
177+ perf : $(BENCH_BINARY ) # # Profile using Linux perf
178+ @echo " Recording perf data..."
179+ @echo " If necessary, run: sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'"
180+ perf record -g ./$(BENCH_BINARY )
181+ @echo " Launching perf report..."
182+ perf report
183+
184+ .PHONY : callgrind
185+ callgrind : $(BENCH_BINARY ) # # Profile using Valgrind's callgrind
186+ valgrind --tool=callgrind --callgrind-out-file=callgrind.out ./$(BENCH_BINARY )
187+ @echo " Callgrind output saved to callgrind.out"
188+ @echo " Use 'kcachegrind callgrind.out' to visualize"
189+
190+ .PHONY : cachegrind
191+ cachegrind : $(BENCH_BINARY ) # # Profile CPU cache usage using Valgrind's cachegrind
192+ valgrind --tool=cachegrind --cachegrind-out-file=cachegrind.out ./$(BENCH_BINARY )
193+ @echo " Cachegrind output saved to cachegrind.out"
194+ @echo " Use 'cg_annotate cachegrind.out' to read the report"
195+
196+ .PHONY : trace
197+ trace : $(BENCH_BINARY ) # # Trace syscalls using strace
198+ strace -o trace.log -T -tt ./$(BENCH_BINARY )
199+ @echo " Syscall trace saved to trace.log"
0 commit comments