aboutsummaryrefslogtreecommitdiff
path: root/src/Makefile
blob: 8538a7d9c0549ca008692f1823d1e5dda8a0fa5e (plain)
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
# Some of this is a little strange for this project,
# but this is just my default Makefile that I use.
#
# Largely inspired by this tutorial
# https://makefiletutorial.com/#top

CC := gcc
CFLAGS := -Wall -Wextra -Werror -std=c99 -pedantic
CFLAGS += -O2
# -DDEBUG is nice, I like 'printf debugging'. Makes it easy.
DEBUG_FLAGS := -g -O0 -DDEBUG -fsanitize=address,undefined \
			   -fno-omit-frame-pointer
LDFLAGS :=
DEBUG_LDFLAGS := -g -fsanitize=address,undefined

# Many of these variables are redundant for this project, but I prefer
# keeping the Makefile in the project root and separating artifacts into
# 'build' and 'target' directories alongside the 'src' directory.
SRC_DIR := .
BIN_DIR := .
OBJ_DIR := build
# Auto-generated dependency files
DEP_DIR := $(OBJ_DIR)/.deps

# Automatically find all C source files
SOURCES := $(wildcard $(SRC_DIR)/*.c)

# Map source files to object files:
#   ./main.c   -> build/main.o
OBJECTS := $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)

# Map object files to dependency files:
#   build/main.o -> build/.deps/main.d
DEPS := $(OBJECTS:$(OBJ_DIR)/%.o=$(DEP_DIR)/%.d)

# Target executable
TARGET := $(BIN_DIR)/detectCycles

.PHONY: all clean debug release help docs
all: $(TARGET)

# Build executable
$(TARGET): $(OBJECTS)
	@mkdir -p $(BIN_DIR)
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
	@echo "Build complete: $@"

# Compile object files
# -MMD  :: 	generate dependency info (exclude system headers)
# -MP   :: 	add phony targets for headers (prevents build errors)
# -MF   :: 	write dependencies to a '.d' file
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
	@mkdir -p $(OBJ_DIR) $(DEP_DIR)
	$(CC) $(CFLAGS) -MMD -MP \
		-MF $(DEP_DIR)/$*.d -c $< -o $@

# Include auto-generated dependency files.
# The leading '-' suppresses errors if the .d files
# do not exist yet e.g. on a clean build.
-include $(DEPS)

# Debug build
debug: CFLAGS := $(filter-out -O2,$(CFLAGS)) $(DEBUG_FLAGS)
debug: LDFLAGS := $(LDFLAGS) $(DEBUG_LDFLAGS)
debug: clean $(TARGET)
	@echo "Debug build complete"

# Release build
release: CFLAGS := $(CFLAGS) -DNDEBUG
release: clean $(TARGET)
	@echo "Release build complete"

# Generate Doxygen documentation
docs:
	@if command -v doxygen >/dev/null 2>&1; then \
		doxygen Doxyfile; \
		echo "Documentation generated"; \
	else \
		echo "Error: doxygen not found. Install it and try again."; \
		exit 1; \
	fi

# Clean build artifacts
clean:
	rm -rf $(OBJ_DIR) $(TARGET)
	@echo "Clean complete"

# Clean documentation
clean-docs:
	rm -rf $(DOC_DIR)
	@echo "Documentation cleaned"

# Help message
help:
	@echo "Available targets:"
	@echo "  all       	- Build the project (default)"
	@echo "  debug     	- Build with debug symbols and no optimization"
	@echo "  release   	- Build optimized release version"
	@echo "  docs      	- Generate Doxygen documentation"
	@echo "  clean     	- Remove all build artifacts"
	@echo "  clean-docs	- Remove generated documentation"
	@echo "  help      	- Display this help message"