# Preamble >> Make config SHELL := bash .ONESHELL: .SHELLFLAGS := -eu -o pipefail -c .DELETE_ON_ERROR: MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules ifeq ($(origin .RECIPEPREFIX), undefined) $(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later) endif .RECIPEPREFIX = > # Custom Make settings PROJECT_NAME := minesweeper CC := gcc -std=c11 CFLAGS := -O3 CWARNINGS := -Werror -Wall -Wextra -pedantic CINCLUDES := -Isrc CLIBS := -lncurses CFORMAT := clang-format -i CTIDY := clang-tidy --checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus-* SOURCE_DIR := src BIN_DIR := bin # Inputs and outputs SOURCES := $(shell find $(SOURCE_DIR) -name *.c) HEADERS := $(shell find $(SOURCE_DIR) -name *.h) OBJECTS := $(patsubst %.c, $(BIN_DIR)/%.o, $(SOURCES)) OUTFILE := $(BIN_DIR)/$(PROJECT_NAME) # Make target config .PHONY: all prelint postlint lint precompile postcompile compile prebuild postbuild build clean run done all: lint compile build run done # Automatically create targets and gather their prerequisites define create-ctarget $1 > @echo -en "\e[37m[\e[34m?\e[37m]\e[0m $$< \e[36m⇒\e[0m $$@" > @mkdir -p $$(dir $$@) > @if $(CC) -o $$@ -c $(CFLAGS) $(CWARNINGS) $(CINCLUDES) $$<>/dev/null 2>&1; > @then echo -e "\r\e[37m[\e[32m✓\e[37m]\e[0m"; > @else echo -e "\r\e[37m[\e[31m✗\e[37m]\e[0m"; > @$(CC) -o $$@ -c $(CFLAGS) $(CWARNINGS) $(CINCLUDES) $$<; > @fi endef compile: precompile $(OBJECTS) postcompile precompile: > @echo -e "\e[1m\e[35mCOMPILING\e[0m" postcompile: > @echo -e "\e[94mDone compiling\e[0m\n" _MM_BACKSLASH := $\\ $(foreach src,$(SOURCES),$(eval $(call create-ctarget,$(filter-out ${_MM_BACKSLASH},$(shell $(CC) -MM -MT $(patsubst %.c, $(BIN_DIR)/%.o, $(src)) $(src)))))) # Link and build outfile build: prebuild $(OUTFILE) postbuild prebuild: > @echo -e "\e[1m\e[35mLINKING\e[0m" postbuild: > @echo -e "\e[94mDone linking\e[0m\n" $(OUTFILE): $(OBJECTS) > @echo -en "\e[37m[\e[34m?\e[37m]\e[0m $^ \e[36m⇒\e[0m $@" > @mkdir -p $(dir $@) > @if $(CC) -o $@ $^ $(CLIBS)>/dev/null 2>&1; > @then echo -e "\r\e[37m[\e[32m✓\e[37m]\e[0m"; > @else echo -e "\r\e[37m[\e[31m✗\e[37m]\e[0m"; > @$(CC) -o $@ $^ $(CLIBS); > @fi done: > @echo -e "\e[1m\e[48;5;28m\e[37mDone!\e[0m" # Run outfile run: $(OUTFILE) > @echo -e "\e[1m\e[93m=== [ PROGRAM OUTPUT ] ===\e[0m" > $(OUTFILE) > @echo -e "\e[1m\e[93m==========================\e[0m\n" # Create linter targets for source and header files define create-linttarget $1 > @echo -en "\e[37m[\e[34m?\e[37m]\e[0m $$<" > @if ! $(CFORMAT) $$<>/dev/null 2>&1; > @then echo -e "\r\e[37m[\e[31m✗\e[37m]\e[0m"; > @$(CFORMAT) $$<; > @fi; > @if $(CTIDY) $$<>/dev/null 2>&1; > @then echo -e "\r\e[37m[\e[32m✓\e[37m]\e[0m"; > @else echo -e "\r\e[37m[\e[31m✗\e[37m]\e[0m"; > @$(CTIDY) $$<; > @fi; > @mkdir -p $$(dir $$@) > @touch $$@ endef # Translate %.(c|h) to bin/%.(c|h)l and create targets for their sources $(foreach src,$(SOURCES),$(eval $(call create-linttarget,$(patsubst %.c, $(BIN_DIR)/%.cl, $(src)): $(src)))) $(foreach header,$(HEADERS),$(eval $(call create-linttarget,$(patsubst %.h, $(BIN_DIR)/%.hl, $(header)): $(header)))) lint: prelint $(patsubst %.h, $(BIN_DIR)/%.hl, $(HEADERS)) $(patsubst %.c, $(BIN_DIR)/%.cl, $(SOURCES)) postlint prelint: > @echo -e "\e[1m\e[35mLINTING\e[0m" postlint: > @echo -e "\e[94mDone linting\e[0m\n" # Clean target clean: > @rm -r $(BIN_DIR)