# https://spin.atomicobject.com/2016/08/26/makefile-c-projects/ was a great # base for this Makefile LIB_FILENAME ?= libvieter.a BUILD_DIR ?= build SRC_DIR ?= src TEST_DIR ?= test INC_DIRS ?= include LIB := $(BUILD_DIR)/$(LIB_FILENAME) SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find $(INC_DIRS) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BINS_TEST := $(OBJS_TEST:%.c.o=%) TARGETS_TEST := $(BINS_TEST:%=test-%) TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%) LIBFLAGS := -larchive INC_FLAGS := $(addprefix -I,$(INC_DIRS)) # -MMD: generate a .d file for every source file. This file can be imported by # make and makes make aware that a header file has been changed, ensuring an # object file is also recompiled if only a header is changed. # -MP: generate a dummy target for every header file (according to the docs it # prevents some errors when removing header files) CFLAGS ?= -MMD -MP -g VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra .PHONY: all all: vieter # =====COMPILATION===== # Utility used by the CI to lint .PHONY: objs objs: $(OBJS) .PHONY: vieter vieter: $(LIB) $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) ar -rcs $@ $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) $(VIETERCFLAGS) -c $< -o $@ # =====TESTING===== .PHONY: test test: $(TARGETS_TEST) .PHONY: test-mem test-mem: $(TARGETS_MEM_TEST) .PHONY: $(TARGETS_TEST) $(TARGETS_TEST): test-%: % ./$^ .PHONY: $(TARGETS_MEM_TEST) $(TARGETS_MEM_TEST): test-mem-%: % valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./$^ .PHONY: build-test build-test: $(BINS_TEST) # For simplicity, we link every object file to each of the test files. This # might be changed later if this starts to become too slow. $(BINS_TEST): %: %.c.o $(LIB) $(CC) \ $^ $(LIBFLAGS) -o $@ # Along with the include directory, each test includes $(TEST_DIR) (which # contains the acutest.h header file), and the src directory of the module it's # testing. This allows tests to access internal methods, which aren't publicly # exposed. $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c mkdir -p $(dir $@) $(CC) $(VIETERCFLAGS) -I$(TEST_DIR) \ -I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \ -c $< -o $@ # =====MAINTENANCE===== .PHONY: lint lint: clang-format -n --Werror $(SRCS) $(SRCS_H) .PHONY: fmt fmt: clang-format -i $(SRCS) $(SRCS_H) .PHONY: clean clean: rm -rf $(BUILD_DIR) # Make make aware of the .d files -include $(DEPS)