From 1d32d43c85b5059d5f162741afbf3ebfcb3a4ebd Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Fri, 16 Feb 2024 23:31:33 -0600 Subject: [PATCH] Added Makefile --- Makefile | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..617364d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,214 @@ +# ============================================================================= +# Cool Makefile for CMake Projects +# ============================================================================= +# +# A Makefile that makes configuring and building CMake projects really simple. + + +# ============================================================================= +# Functions +# ============================================================================= + +define to_lower_case +$(shell echo $(1) | tr '[:upper:]' '[:lower:]') +endef + +define find_in_list +$(shell resolved_value=$$(for candidate in $(2); do \ + if [ "$(call to_lower_case,$(1))" = "$$(echo $$candidate | tr '[:upper:]' '[:lower:]')" ]; then \ + echo $$candidate; \ + fi; \ + done); \ + echo "$${resolved_value:-$(1)}";) +endef + +define exists_in_list +$(shell resolved_value=$$(for candidate in $(2); do \ + if [ "$(call to_lower_case,$(1))" = "$$(echo $$candidate | tr '[:upper:]' '[:lower:]')" ]; then \ + echo true; \ + fi; \ + done); \ + echo "$${resolved_value:-false}";) +endef + +# ============================================================================= +# Variables +# ============================================================================= + +project_name:=? +project_lang:=? +type:=debug +generator:=$(shell if [ -x "$$(which ninja)" ]; then echo "Ninja"; else echo "Unix Makefiles"; fi) +build_path:=$(shell if [ "$(call to_lower_case,$(generator))" = "xcode" ]; then echo "xcode-build"; else echo "cmake-build-$(call to_lower_case,$(type))"; fi) + +# ============================================================================= +# Constants +# ============================================================================= + +override cores:=$(shell if [ "$$(uname)" = "Darwin" ]; then sysctl -n hw.ncpu; elif [ "$$(uname)" = "Linux" ]; then nproc --all; else echo 1; fi) +override valid_generators:="Unix Makefiles" "Ninja" "Xcode" +override valid_types:=Release Debug RelWithDebInfo MinSizeRel +override debug_types:=Debug RelWithDebInfo + +override cmake_build_type_arg:=$(call find_in_list,$(type),$(valid_types)) +override cmake_generator_arg:=$(call find_in_list,$(generator),$(valid_generators)) +override is_debug:=$(call exists_in_list,$(type),$(debug_types)) + +# ============================================================================= +# Utility targets +# ============================================================================= + +# All targets are phony in this Makefile +.PHONY : $(shell egrep "^[A-Za-z0-9_-]+\:([^\=]|$$)" $(lastword $(MAKEFILE_LIST)) | sed -E 's/(.*):.*/\1/g') + +# Default target is build +all: build + +#: Displays this message +help: + @echo "Usage:" + @echo + @echo " make type= generator= " + @echo + @echo "Targets:" + @egrep -B1 "^[A-Za-z0-9_-]+\:([^\=]|$$)" $(lastword $(MAKEFILE_LIST)) \ + | grep -v -- -- \ + | sed 'N;s/\n/###/' \ + | sed -n 's/^#: \(.*\)###\(.*\):.*/ \2###\1/p' \ + | column -t -s '###' + +#: List available build types +list-types: + @for type in $(valid_types); do echo $$type; done + +#: List available generators +list-generators: + @for generator in $(valid_generators); do echo $$generator; done; + +validate-type: +ifneq ($(cmake_build_type_arg),$(filter $(cmake_build_type_arg),$(valid_types))) + @echo "Invalid build type: $(cmake_build_type_arg). Valid types are:" >&2; + @for type in $(valid_types); do echo - $$type >&2; done + @exit 1; +else ifeq ($(strip $(cmake_build_type_arg)),) + @echo "Empty build type. Valid types are:" >&2; + @for type in $(valid_types); do echo - $$type >&2; done + @exit 1; +else + @: +endif + +validate-generator: +ifneq ("$(cmake_generator_arg)",$(filter "$(cmake_generator_arg)",$(valid_generators))) + @echo "Unsupported generator: $(cmake_generator_arg). Valid generators are:" >&2; + @for generator in $(valid_generators); do echo - $$generator >&2; done; + @exit 1; +else ifeq ($(strip $(cmake_generator_arg)),) + @echo "Empty CMake generator. Valid generators are:" >&2; + @for generator in $(valid_generators); do echo - $$generator >&2; done; + @exit 1; +else + @: +endif + +# ============================================================================= +# Build targets +# ============================================================================= + +#: Delete the build path for the currently selected type and generator +clean: validate-type + @rm -rf "./$(build_path)"; + +#: Delete all build paths for all types and generators +clean-all: + @for generator in $(valid_generators); do \ + for type in $(valid_types); do \ + $(MAKE) generator="$$generator" type=$$type clean; \ + done; \ + done; + +#: Initialise an empty CMake project +init: + @if [ ! -f CMakeLists.txt ]; then \ + project_name=$(project_name); \ + if [ "$$project_name" = "?" ]; then \ + read -p "Enter project name: " project_name; \ + fi; \ + project_lang=$(project_lang); \ + if [ "$$project_lang" = "?" ]; then \ + read -p "Enter project language (c|cpp): " project_lang; \ + fi; \ + project_lang=$$(echo $$project_lang | tr '[:upper:]' '[:lower:]' | sed -E -e 's/^(c\+\+|cxx)$$/cpp/'); \ + if [ "$$project_lang" = "c" ]; then \ + echo "#include \"stdio.h\"\n\nint main() {\n\tprintf(\"%s\", \"hello, world!\");\n\treturn 0;\n}\n" >$$project_name.$$project_lang; \ + elif [ "$$project_lang" = "cpp" ]; then \ + echo "#include \n\nint main() {\n\tstd::cout << \"hello, world!\" << std::endl;\n\treturn 0;\n}\n" >$$project_name.$$project_lang; \ + else \ + echo "Unsupported language type" >&2; \ + exit 1; \ + fi; \ + echo "project($$project_name)\n\nadd_executable($$project_name $$project_name.$$project_lang)\n\n" >CMakeLists.txt; \ + fi; + +#: Configure the project for the selected type and generator +configure: validate-type validate-generator init +ifeq ($(cmake_generator_arg), Xcode) + @cmake -S . -B $(build_path) \ + -G "$(cmake_generator_arg)"; +else + @cmake -S . -B $(build_path) \ + -DCMAKE_BUILD_TYPE=$(cmake_build_type_arg) \ + -G "$(cmake_generator_arg)"; +endif + +#: Build the project for the selected type and generator +build: configure +ifeq ($(cmake_generator_arg), Xcode) + @cmake --build $(build_path) --config $(cmake_build_type_arg); +else + @cmake --build $(build_path) -- -j$(cores); +endif + +#: Install the project for the selected type and generator +install: build +ifeq ($(cmake_generator_arg), Xcode) + @cmake --install $(build_path) --config $(cmake_build_type_arg); +else + @cmake --install $(build_path); +endif + +#: Run the application +run: build + @app_name=$$(cmake -S . -B "$(build_path)" --trace 2>&1 \ + | egrep -i add_executable \ + | sort -r \ + | head -n 1 \ + | sed -E 's/.*add_executable.[[:space:]]*([A-Za-z0-9\\"_\.+-]+)[[:space:]].*/\1/'); \ + if [ "$(call to_lower_case,$(generator))" = "xcode" ]; then \ + cd $(build_path)/$(cmake_build_type_arg) && ./$$app_name; \ + else \ + cd $(build_path) && ./$$app_name; \ + fi + @echo "\nProcessed finished with exit code $$?" + + +# ============================================================================= +# Makefile debugging targets +# ============================================================================= + +list-variables: + @echo $(foreach \ + v, \ + $(.VARIABLES), \ + $(if \ + $(filter file, $(origin $(v))), \ + \\n$(v)=$($(v)), \ + $(if \ + $(filter command line, $(origin $(v))), \ + \\n$(v)=$($(v)), \ + $(if \ + $(filter override, $(origin $(v))), \ + \\n$(v)=$($(v)))))) | egrep -v '^$$' | egrep -v '=\s*$$' | sort + +list-targets: + @egrep "^[A-Za-z0-9_-]+\:([^\=]|$$)" $(lastword $(MAKEFILE_LIST)) | sed -E 's/(.*):.*/\1/g' | sort