Created
September 2, 2015 00:46
-
-
Save Redchards/271f61580c0883524545 to your computer and use it in GitHub Desktop.
What an ugly makefile ! But it was damn fun to write :)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Damn is this makefile ugly ! And buggy tooo ! | |
# But it was sure fun to write :P | |
# The shell that the make will use to execute shell commands. | |
# It can be set to any decent shell without any problem. | |
SHELL:=/bin/bash | |
# We only define the C++ compiler, and it will take care of compiling both C and C++ files. | |
# We also set the linker (LD) to the same value, as most of the compilers do it through the same command. | |
CXX= clang++ | |
LD= $(CXX) | |
# The final executable, or library, name. | |
EXEC:= Arena | |
# Current configuration. Debug by default. | |
CONFIG=debug | |
# Various directory path. | |
# As nothing is hardcoded, the values can be set to the liking of the user, without breaking something. | |
LIBDIR:= lib | |
OBJDIR:= obj | |
SRCDIR:= src | |
INCLDIR:= include | |
BINDIR:= bin | |
SCANDIR:= scan | |
# Basic C and C++ flags | |
CFLAGS= -W -Wall -Wextra | |
CXXFLAGS= $(CFLAGS) -std=c++1y | |
CFLAGS+= -std=c11 | |
# Flags used only for debug mod | |
DEBUGFLAGS:= -g -O0 | |
# Flags used only for release mod | |
RELEASEFLAGS:= -O3 | |
# Flags used only for analyzis mod | |
ANALYZERFLAGS:= --analyze -Xanalyzer -analyzer-output=html -o $(SCANDIR) | |
# Flags used only for bitcode compilation (by LLVM/clang) | |
JITFLAGS:= -emit-llvm -S -fno-use-cxa-atexit | |
# Character used to make the makefile silent. | |
SILENT:=@ | |
# Flags used to create dependencies | |
MAKEDEPEND:= -MMD | |
# Flags used by the linker | |
LDFLAGS:= | |
# This variable will determine whether we will use native or jit execution. | |
# The values are either "native" or "jit". | |
# The value "jit" is only valid for the clang compiler. | |
EXECUTION:=native | |
# Turns on/off the verbose output of the makefile. | |
# The values are either "yes" or "no" | |
VERBOSE:=no | |
# Extensions of the different types of file | |
OBJEXT:=o | |
DEPEXT:=d | |
CEXT:=c | |
CXXEXT:=cxx | |
ifeq ($(EXECUTION), jit) | |
ifeq ($(CONFIG), analysis) | |
$(warning Warning : analysis mod is independant of execution mod. No bitcode will be generated) | |
endif | |
ifeq (, $(filter $(CXX), clang clang++)) | |
$(error Error : attempt to emit llvm IR, but not using clang) | |
endif | |
CXXFLAGS+= $(JITFLAGS) | |
CFLAGS+= $(JITFLAGS) | |
LD= llvm-link | |
EXEC:= $(addprefix $(EXEC).,bc) | |
OBJEXT= bco | |
endif | |
ifeq ($(CONFIG), analysis) | |
ifeq (, $(filter $(CXX), clang clang++)) | |
$(error Error : attempt to enable analysis, but not using clang) | |
endif | |
CXXFLAGS+= $(ANALYZERFLAGS) | |
endif | |
ifeq ($(VERBOSE), yes) | |
SILENT= | |
endif | |
SRC:=$(shell find $(SRCDIR) -name '*.$(CEXT)' -o -name '*.$(CXXEXT)') | |
OBJ=$(subst src/,,$(SRC:.$(CEXT)=.$(OBJEXT))) | |
OBJ:=$(subst src/,,$(OBJ:.$(CXXEXT)=.$(OBJEXT))) | |
.SECONDEXPANSION: | |
OBJS=$(addprefix obj/$(CONFIG)/, $(OBJ)) | |
ALLOBJ=$(addprefix obj/, $(OBJ)) | |
DEPS:=$(OBJ:.$(OBJEXT)=.$(DEPEXT)) | |
# Define the path where the result will be outputted | |
OUTPATH=$(if $(filter $(CONFIG), analysis),$(SCANDIR),$(BINDIR)/$(CONFIG)) | |
# .PHONY targets. | |
.PHONY: clean cleanall debug release analyze | |
.SECONDEXPANSION: | |
all: pre-build $(EXEC) | |
# Target specific variable value. | |
# These targets are used to do less typing while invoking the make with a special config. | |
debug: CONFIG:=debug | |
debug: pre-build $(EXEC) | |
release: CONFIG:=release | |
release: pre-build $(EXEC) | |
analysis: CONFIG=analysis | |
analysis: all | |
# The first dependency of the "all" target. | |
# It takes care of selecting the right flags depending on the config. | |
# And yhea, I know eval is evil, but this will be executed only once in the makefile, sooo ... no big deal :) | |
pre-build: build-info build.gen | |
$(eval FLAGS:=$(shell \ | |
export CONFIG="$(CONFIG)";\ | |
export RELEASEFLAGS="$(RELEASEFLAGS)";\ | |
export DEBUGFLAGS="$(DEBUGFLAGS)";\ | |
export ANALYZERFLAGS="$(ANALYZERFLAGS)";\ | |
\ | |
if [ $$CONFIG == "debug" ]; then\ | |
echo $$DEBUGFLAGS;\ | |
elif [ $$CONFIG == "release" ]; then\ | |
echo $$RELEASEFLAGS;\ | |
elif [ $$CONFIG == "analysis" ]; then\ | |
echo $$ANALYZERFLAGS;\ | |
fi)) | |
$(if $(FLAGS),,$(error Error : configuration "$(CONFIG)" is invalid !)) | |
OK= | |
-include build.gen | |
build.gen: | |
@echo "$(EXEC) :: $(OBJS)" > build.gen | |
ifeq ($(EXECUTION), jit) | |
@printf "\t$(SILENT) $(LD) $(EXEC) -o $(BINDIR)/$(CONFIG)/$(EXEC)\n" >> build.gen | |
else | |
$(if $(filter $(CONFIG), analysis),\ | |
printf 'echo "No executable code output for analysis"',\ | |
printf '\t@ mkdir -p $(BINDIR)/$(CONFIG)\n\t$(LD) -o $(BINDIR)/$(CONFIG)/$(EXEC) $$^ $(LDFLAGS)\n') >> build.gen | |
endif | |
@echo "" >> build.gen | |
@echo '-include $$(addprefix obj/$(CONFIG)/,$(DEPS))' >> build.gen | |
@echo "PERCENT=%" >> build.gen | |
@printf "$(OBJDIR)/$(CONFIG)/"'$$(PERCENT)'".$(OBJEXT): $(SRCDIR)/"'$$(PERCENT)'".$(CEXT)\n\ | |
$$(eval OK=1)\n\ | |
$(SILENT) mkdir -p $(OBJDIR)/$(CONFIG)/"'$$(dir $$*)'"\n\ | |
$(SILENT) $(CXX) -I$(INCLDIR) -x c -o $(OBJDIR)/$(CONFIG)/"'$$*'".$(OBJEXT) -c $$< $(CFLAGS) $(FLAGS) $(MAKEDEPEND)\n" >> build.gen | |
@printf "$(OBJDIR)/$(CONFIG)/"'$$(PERCENT)'".$(OBJEXT): $(SRCDIR)/"'$$(PERCENT)'".$(CXXEXT)\n\ | |
$$(eval OK=1)\n\ | |
$(SILENT) mkdir -p $(OBJDIR)/$(CONFIG)/"'$$(dir $$*)'"\n\ | |
$(SILENT) $(CXX) -I$(INCLDIR) -x c++ -o $(OBJDIR)/$(CONFIG)/"'$$*'".$(OBJEXT) -c $$< $(CXXFLAGS) $(FLAGS) $(MAKEDEPEND)\n" >> build.gen | |
# This target is the main target. | |
# It will link all file generated by its dependencies together | |
#$(EXEC): override ALLOBJ=$(addprefix obj/$(CONFIG)/, $(OBJ)) | |
$(EXEC) :: | |
@ $(if $(OK)$(filter $(CONFIG), analysis),\ | |
echo -e "\e[1m\e[32mGeneration successful !\e[0m",\ | |
echo -e "\e[1m\e[32mNothing to do, everything up to date !\e[0m") | |
ifeq ($(EXECUTION), native) | |
$(SILENT) $(if $(filter $(CONFIG), release),\ | |
echo "Stripping binary ...";\ | |
strip $(EXEC);\ | |
echo "Done !",) | |
else | |
@ $(if $(OK),\ | |
echo -e "\e[1m\e[92mBitcode generated\e[0m",) | |
endif | |
@ $(if $(OK)$(filter $(CONFIG), analysis),\ | |
echo -e "Resulting file : \e[1m\e[92m$(EXEC)\e[0m";\ | |
echo -e "See the result in the following directory : \e[1m\e[96m$(OUTPATH)\e[0m",) | |
clean: | |
$(SILENT) rm -rf $(OBJDIR)/* | |
cleanall: clean | |
$(SILENT) rm -rf $(EXEC) | |
$(SILENT) rm -f ./build.gen | |
# The summary of the upcoming compilation configuration printed at the begining. | |
# It could be extended, but it is sufficient like this. | |
HEADER_MSG:="COMPILATION OF PROJECT : $(EXEC)" | |
build-info: | |
@echo | |
$(shell printf "#%.0s" $$(seq 1 $$(tput cols))) | |
@echo -e "\e[1m\e[91m$(shell export HEADER_MSG=$(HEADER_MSG); printf " %.0s" $$(seq 1 $$(($$(tput cols)/2 - $${#HEADER_MSG}/2)))) $(HEADER_MSG)\e[0m" | |
@echo | |
@echo -e "Generation configuration is \e[1m\e[32m$(CONFIG)\e[0m" | |
@echo | |
@echo -e "Sources directory search path is : \e[1m\e[96m$(SRCDIR)\e[0m (SRCDIR)" | |
@echo -e "Includes directory search path is : \e[1m\e[96m$(INCLDIR)\e[0m (INCLDIR)" | |
@echo -e "Libraries directory search path is : \e[1m\e[96m$(LIBDIR)\e[0m (LIBDIR)" | |
@echo -e "Objects directory is : \e[1m\e[96m$(OBJDIR)\e[0m (OBJDIR)" | |
@echo -e "The binary output path is : \e[1m\e[96m$(OUTPATH)\e[0m" | |
@echo | |
$(shell printf "#%.0s" $$(seq 1 $$(tput cols))) | |
@echo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment