# https://spin.atomicobject.com/2016/08/26/makefile-c-projects/ was a great # base for this Makefile -include config.mk LIB_ARCHIVE := $(BUILD_DIR)/lib$(LIB).a SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find include -iname '*.h' SRCS_H_INTERNAL != find $(SRC_DIR) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' SRCS_EXAMPLE != find '$(EXAMPLE_DIR)' -iname '*.c' OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) OBJS_EXAMPLE := $(SRCS_EXAMPLE:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BIN_TEST := $(BUILD_DIR)/$(TEST_DIR)/runner BINS_EXAMPLE := $(OBJS_EXAMPLE:%.c.o=%) _CFLAGS := $(addprefix -I,$(INC_DIRS)) $(CFLAGS) -Wall -Wextra # =====COMPILATION===== # Utility used by the CI to lint .PHONY: lib $(LIB_ARCHIVE): $(OBJS) ar -rcs $@ $^ .PHONY: objs objs: $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) -c $(_CFLAGS) $< -o $@ # =====TESTING===== .PHONY: test test: $(BIN_TEST) './$^' .PHONY: test-mem test-mem: $(BIN_TEST) valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full './$^' .PHONY: build-test build-test: $(BIN_TEST) $(BIN_TEST): $(OBJS_TEST) $(LIB_ARCHIVE) $(CC) -o $@ $^ $(_LDFLAGS) # 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) $(_CFLAGS) -I$(TEST_DIR) \ -I$(SRC_DIR)/_include \ -c $< -o $@ # =====EXAMPLES===== .PHONY: build-example build-example: $(BINS_EXAMPLE) $(BINS_EXAMPLE): %: %.c.o $(LIB_ARCHIVE) $(CC) $(LDFLAGS) \ $^ -o $@ # Example binaries link the resulting library $(BUILD_DIR)/$(EXAMPLE_DIR)/%.c.o: $(EXAMPLE_DIR)/%.c mkdir -p $(dir $@) $(CC) $(_CFLAGS) -I$(PUB_INC_DIR) -c $< -o $@ # =====MAINTENANCE===== .PHONY: lint lint: @ clang-format -n --Werror \ $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') \ $(shell find '$(SRC_DIR)/_include/$(LIB)' -iname '*.h') \ $(shell find 'include/$(LIB)' -iname '*.h') .PHONY: fmt fmt: @ clang-format -i \ $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') \ $(shell find '$(SRC_DIR)/_include/$(LIB)' -iname '*.h') \ $(shell find 'include/$(LIB)' -iname '*.h') .PHONY: check check: @ mkdir -p $(BUILD_DIR)/cppcheck @ cppcheck \ $(addprefix -I,$(INC_DIRS)) \ --cppcheck-build-dir=$(BUILD_DIR)/cppcheck \ --error-exitcode=1 \ --enable=warning,style \ --inline-suppr \ --check-level=exhaustive \ --quiet \ -j$(shell nproc) \ $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') .PHONY: clean clean: rm -rf '$(BUILD_DIR)' .PHONY: bear bear: clean bear -- make bear --append -- make build-test bear --append -- make build-example # Make make aware of the .d files -include $(DEPS)