Skip to content

Instantly share code, notes, and snippets.

@Redchards
Last active September 4, 2015 18:13
Show Gist options
  • Save Redchards/4d3d3e2ef7ed0cdc5137 to your computer and use it in GitHub Desktop.
Save Redchards/4d3d3e2ef7ed0cdc5137 to your computer and use it in GitHub Desktop.
A nice general purpose makefile ideal for testing and rapid prototyping of C and/or C++ language, without having to bother about compilation options too much. And even if one needs to add some options, it could be done easily. Of course, it's not a good idea to use it for bigger projects. Please report any bug you might find !
# This makefile is geared towards begginers and people who want fast prototyping and/or testing,
# and even if not willing to take the time to write a makefile, wants to have a configurable and yet
# solid solution.
# So yes, this makefile is general purpose, but really only useful for small to average projects.
# Indeed, bigger projects will require build mecanism far more advanced than such a simple makefile
# (or beware of everlasting compilation time ...).
# So, even if this makefile should be more than enough in general, there's few caveheats you should
# be aware of.
# First of all, to save typing and hassle, this makefile do a bunch of things "automagically". This, of course,
# includes some overhead (but usually not that much). So if you want really fast iteration time, this may be not
# the right solution.
# Second, even if I tried to make configuration as easy as possible, some more advanced task (cross compiling) will
# require some tweaks. It's explained in the "HOWTO" below.
# Finally, when you type the make command, some arguments will not be treated as target by the script
# For example, in the command "make clean release", clean will be the target rule, and release will do
# nothing but setting the configuration to release mod. So only the first argument count as a target here.
# But this case is order-sensitive, as doing "make release clean" will first execute the release rule,
# then clean the temporaries produced by this rule. This is a nice side effect, but this can be confusing
# if you don't know this at first.
#
# HOWTO : - you can set the project name by setting the "EXEC" variable to your liking. By default, it is
# the name of the directory the makefile is in.
# - the default directory structure required by the makefile is : "src" for the source files and
# "include" for the header files. You can change this by setting "SRCDIR" and "INCLDIR" variables to your
# liking.
# - make clean is the only command which is order dependent (in order to keep the side effect
# explained above).
# - configurations are autogenerated from ALLPLATFORMS and ALLCONFIGS lists. It takes the form of
# config-platform. So, if you want to compile in release mod for 32 bit intel (x86), you should type
# "make release-x86".
# - compilation mod is by default debug, and platform x86. You can adjust these values
# by changind DEFAULTCONFIG and DEFAULTPLATFORM variables.
# - to compile to llvm bitcode, juste type "make xxx jit" where xxx is the configuration you want.
# same thing for analysis mod.
# - use "make cleantmp" to clean all objs and dependencies.
# - use "make cleanall" to clean absolutly everything, final files included.
# - build-info rule is nice (the one which is giving configuration info at the beggining) is nice,
# but can introduce an unwanted performance overhead. Using the option "noinfo" allows to disable it.
# - the flags adopt the following convention : the word is transformed to uppercase, and the word
# "FLAGS" is appended. So, for example, to flags corresponding to the "release" mod is "RELEASEFLAGS".
# If flags are intended to be used for linkin, "LDFLAGS" is appended. The only case we got this situation
# is with library flags, for example, the linking flags corresponding to "shared" mod is "SHAREDLDFLAGS"
# - if you add new mods or configurations, remember to add new flags accordingly. For example, if one
# was to add "armv7" platform support, he would change the compiler (CXX) to a cross compiler and create a
# ARMV7FLAGS variable, which would contains the required compiler flags for armvs7 platform. Everything will then
# setup automatically. Maybe I will add later and option to set the compiler accordingly to the wanted platform if
# requested.
# 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)
JITLD:= llvm-link
# The final executable, or library, name.
EXEC:=$(notdir $(shell pwd))
# 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
# Extensions of the different types of file
EXEEXT:=
OBJEXT:=o
DEPEXT:=d
CEXT:=c
CXXEXT:=cxx
STATICLIBEXT:=a
SHAREDLIBEXT:=so
# Basic C and C++ flags
FLAGS:= -W -Wall -Wextra
CFLAGS= $(FLAGS) -std=c11
CXXFLAGS= $(FLAGS) -std=c++1y
# Flags used only for debug mod
DEBUGFLAGS:= -g -O0
# Flags used only for release mod
RELEASEFLAGS:= -O3
# Flags used only for analyzis mod
ANALYSISFLAGS:= --analyze -Xanalyzer -analyzer-output=html -o $(SCANDIR)
# Flags used for different platforms
X86FLAGS:= -m32
X64FLAGS:= -m64
# Flags used to create dependencies
DEPENDFLAGS:= -MMD
# Flags used by the linker
LDFLAGS:=
# Flags used only for bitcode compilation (by LLVM/clang)
JITFLAGS:= -emit-llvm -S -fno-use-cxa-atexit
# Flags to use for different library mod.
STATICLDFLAGS:= rcs
SHAREDFLAGS:= -fPIC
SHAREDLDFLAGS:= -shared
# Flags to add verbose linker and compiler output
# These are the only flags which do not follow the convention explained at the beggining
VERBOSEFLAGS:= -v
VERBOSELDFLAGS:= -v
# Some helper functions
define to_lower
$(strip $(shell echo $1 | tr '[:upper:]' '[:lower:]'))
endef
define to_upper
$(strip $(shell echo $1 | tr '[:lower:]' '[:upper:]'))
endef
# Returns the flags following the convention explained at the beggining
define get_flags
$(strip $($(call to_upper, $1)FLAGS))
endef
# Returns the linking flags following the convention explained at the beggining
define get_ldflags
$(strip $($(call to_upper, $1)LDFLAGS))
endef
# This variable will determine whether we will use native or JIT execution.
# JIT execution will produce llvm bitcode, which can be executed by the llvm execution engine.
# The command "lli" is the simplest way to run them.
# The values are either "native" or "jit".
# The value "jit" is only valid for the clang compiler.
override ALLEXECUTIONS:=native jit
override DEFAULTEXECUTION:=native
override PASSEDEXECUTION:=$(filter $(MAKECMDGOALS), $(ALLEXECUTIONS))
ifeq ($(words $(PASSEDEXECUTION)), 0)
EXECUTION:=$(DEFAULTEXECUTION)
else ifeq ($(words $(PASSEDEXECUTION)), 1)
EXECUTION:=$(PASSEDEXECUTION)
else
$(error Error : multiple arguments or invalid execution mod !)
endif
FLAGS+=$(call get_flags, $(EXECUTION))
# Defining supported platforms.
# Of course, more can be added
override ALLPLATFORMS:=x86 x64
ifeq ($(shell getconf LONG_BIT), 64)
override DEFAULTPLATFORM:=x64
else ifeq ($(shell getconf LONG_BIT), 32)
override DEFAULTPLATFORM:=x86
endif
# List of all available compilation mods.
override ALLCONFIGS:=debug release analysis
# The default config.
override DEFAULTCONFIG:=debug
# Build the configuration names, following the convention explained at the beggining.
override CONFIG_PLATFORM:=$(foreach CONFIG, $(filter-out analysis, $(ALLCONFIGS)),$(addprefix $(CONFIG)-, $(ALLPLATFORMS)))
# Append compilation mods list to configuration names, to allow used to not specify the platform (choosing the default).
override CONFIG_PLATFORM+=$(ALLCONFIGS)
# Parse the user input, and extract build mod and platform.
override PASSEDCONFIG:=$(filter $(MAKECMDGOALS), $(CONFIG_PLATFORM))
ifeq ($(words $(PASSEDCONFIG)), 0)
CONFIG:=$(DEFAULTCONFIG)
PLATFORM:=$(DEFAULTPLATFORM)
else ifeq ($(words $(PASSEDCONFIG)), 1)
CONFIG:=$(filter $(foreach PLATFORM, $(ALLPLATFORMS),$(patsubst %-$(PLATFORM),%,$(PASSEDCONFIG))), $(ALLCONFIGS))
PLATFORM:=$(filter $(foreach PLATFORM, $(ALLPLATFORMS),$(patsubst %-$(PLATFORM),$(PLATFORM),$(PASSEDCONFIG))), $(ALLPLATFORMS))
else
$(error Error : multiple configuration or invalid configuration mod !)
endif
# If no platform was defined, and the passed mod wasn't analysis, we set the platform to the default value.
ifeq ($(PLATFORM),)
ifneq ($(CONFIG), analysis)
PLATFORM:=$(DEFAULTPLATFORM)
endif
endif
FLAGS+=$(call get_flags, $(CONFIG))
private TMP:=$(call get_flags, $(PLATFORM))
FLAGS+=$(TMP)
# If the compilation mod is not JIT, then the linker also need platform flags infos.
ifneq ($(EXECUTION), jit)
LDFLAGS+=$(TMP)
endif
# If we got only a trailing dash (compilation mod is analysis), remove it and only assign the compilaion mod.
# Else, build congname accordingly.
override CONFIGNAME:=$(patsubst %-, %, $(CONFIG)-$(PLATFORM))
# Set options for execution mods.
# Native execution mod do not change anything.
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 LLVM/clang)
endif
LD= $(JITLD)
EXEC:= $(addprefix $(EXEC).,bc)
OBJEXT:= bco
endif
ifeq ($(CONFIG), analysis)
ifeq (, $(filter $(CXX), clang clang++))
$(error Error : attempt to enable analysis, but not using LLVM/clang)
endif
endif
# Parse library configuration, and set options accordingly.
private override LIBTYPES:=static shared
private override PASSEDLIBTYPE:=$(filter $(MAKECMDGOALS), $(LIBTYPES))
ifneq ($(PASSEDLIBTYPE),)
ifeq ($(EXECUTION), jit)
$(error : Can't build bitcode library ! Please select native execution mode (default))
endif
ifneq ($(words $(PASSEDLIBTYPE)), 1)
$(error : Can't build multiple type of lib at the same time. Please select only one !)
else ifeq ($(PASSEDLIBTYPE), static)
# Static libs are really just archives of objects, so no platform information is needed
# (it's already contained in the objects)
LDFLAGS:=$(filter-out $(call get_flags, $(PLATFORM)), $(LDFLAGS))$(call get_ldflags, static)
LD:=ar
EXEC:=lib$(call to_lower, $(EXEC)).$(STATICLIBEXT)
else ifeq ($(PASSEDLIBTYPE), shared)
FLAGS+=$(call get_flags, shared)
LDFLAGS+=$(call get_ldflags, shared)
EXEC:=lib$(call to_lower, $(EXEC)).$(SHAREDLIBEXT)
# Change objs extension because we can't use .o files produced by default rules to produce a shared library,
# and we don't want to clean object if we build shared lib after static lib.
# Dynamic linking requires dynamic reallocation (-fPIC flag for gcc and clang).
OBJEXT:=osh
endif
# If the exe extension is not empty (on windows for example), add it to the exe name.
else ifneq ($(EXEEXT),)
EXEC:=$(EXEC).$(EXEEXT)
endif
# Character used to make the makefile silent.
SILENT:=@
# Turns on/off the verbose output of the makefile.
# The values are either "none", "some", "detail" or "all"
# Verbose should turn verbose options for compiler and linker too !
VERBOSE:=none
ifneq ($(filter $(VERBOSE), some detail all),)
ifneq ($(filter $(VERBOSE), detail all),)
FLAGS+=$(VERBOSEFLAGS)
LDFLAGS+=$(VERBOSELDFLAGS)
ifeq ($(VERBOSE), all)
SHELL_:=$(SHELL)
SHELL=$(warning [$@])$(SHELL_) -x
endif
endif
SILENT:=
endif
# Creation of sources, objects and dependencies list.
SRC:=$(shell find $(SRCDIR) -name '*.$(CEXT)' -o -name '*.$(CXXEXT)')
OBJ:=$(subst src/,,$(SRC:.$(CEXT)=.$(OBJEXT)))
OBJ:=$(subst src/,,$(OBJ:.$(CXXEXT)=.$(OBJEXT)))
OBJS=$(addprefix obj/$(PLATFORM)/$(CONFIG)/, $(OBJ))
DEPS:=$(OBJ:.$(OBJEXT)=.$(DEPEXT))
# Define the path where the result will be outputted
OUTPATH:=$(if $(filter $(CONFIG), analysis),$(SCANDIR),$(BINDIR)/$(PLATFORM)/$(CONFIG))
# .PHONY targets.
.PHONY: clean cleantmp cleanall $(CONFIG_PLATFORM) $(ALLEXECUTIONS)
# Rule "all". All other first rules depends on it.
all: build-info $(OUTPATH)/$(EXEC)
@$(if $(OK),,\
printf "\e[1m\e[32mNothing to do, everything is up to date !\e[0m\n\n")
# If the first option is not clean, we call the "all" rule.
ifeq ($(filter $(firstword $(MAKECMDGOALS)), clean),)
$(CONFIG_PLATFORM): all
else
$(CONFIG_PLATFORM):
@ printf "Done !"
endif
# Foo rules for options.
# Using the "@#" to make a "silent comment". Yhea, strange enough I figured that myself ...
# If the first option is not an execution, we don't do anything.
# Else, we initiate the build.
ifeq ($(filter $(firstword $(MAKECMDGOALS)), $(ALLEXECUTIONS)),)
$(ALLEXECUTIONS):
@#
else
$(ALLEXECUTIONS): $(CONFIGNAME)
endif
# If the first otion is not a library type, we don't do anything.
# Else, we initiate the build.
ifeq ($(filter $(firstword $(MAKECMDGOALS)), $(LIBTYPES)),)
$(LIBTYPES):
@#
else
$(LIBTYPES): $(CONFIGNAME)
endif
# Do we want build-info to run ?
ifeq ($(firstword $(MAKECMDGOALS)), noinfo)
noinfo : $(CONFIGNAME)
else
noinfo:
@#
endif
# This is the main rule.
# It will link all file generated by its dependencies together.
-include $(addprefix obj/$(PLATFORM)/$(CONFIG)/,$(DEPS))
$(OUTPATH)/$(EXEC) : $(OBJS)
$(eval OK=1)
$(SILENT) mkdir -p $(OUTPATH)
ifeq ($(EXECUTION), jit)
$(SILENT) $(LD) $(LDFLAGS) $^ -o $(OUTPATH)/$(EXEC)
else
$(if $(filter $(CONFIG), analysis),\
@ printf "No executable code output for analysis",\
$(SILENT) printf "Linking binary ... ";\
$(LD) $(LDFLAGS) -o $(OUTPATH)/$(EXEC) $^;\
printf "Done !\n")
endif
@ printf "\e[1m\e[32mGeneration successful !\e[0m\n"
ifeq ($(EXECUTION), native)
$(SILENT) $(if $(filter $(CONFIG), release),\
printf "Stripping binary ... ";\
strip $(OUTPATH)/$(EXEC);\
echo "Done !",)
else
@ printf "\e[1m\e[92mBitcode generated\e[0m\n"
endif
@ printf "Resulting file : \e[1m\e[92m$(EXEC)\e[0m\n\
See the result in the following directory : \e[1m\e[96m$(OUTPATH)\e[0m\n"
# Eval might be a bottleneck here for larger projects
# Generation of obj file for C source
$(OBJDIR)/$(PLATFORM)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(@D)
$(SILENT) $(CXX) -I$(INCLDIR) -x c -o $@ -c $< $(CFLAGS) $(DEPENDFLAGS)
# Generation of obj file for C++ source
$(OBJDIR)/$(PLATFORM)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CXXEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(@D)
$(SILENT) $(CXX) -I$(INCLDIR) -x c++ -o $@ -c $< $(CXXFLAGS) $(DEPENDFLAGS)
# We want the following rule to run in a sequential way (we don't want the building and the cleaning mixed together)
.NOTPARALLEL:
# The clean rule will conditionally delete temporary objects.
# For example, if you type "make clean debug", it will clean only debug native objects (not bitcodes ones).
# The same effect can be achieve by typing "make clean", as debug is the default configuration,
# in the original makefile at least
clean:
@echo "Cleaning $(CONFIG) $(EXECUTION) temporary objects ..."
$(SILENT) find $(OBJDIR)/$(PLATFORM)/$(CONFIG) -name "*.$(OBJEXT)" -type f -delete -o -name "*.$(DEPEXT)" -type f -delete
# Clean every tmp files produced by different compilations.
cleantmp:
$(SILENT) rm -rf $(OBJDIR)/*
# Clean absolutly everything, including produced binaries.
cleanall: cleantmp
$(SILENT) rm -f ./build.gen
$(SILENT) rm -rf $(BINDIR)/*
# The summary of the upcoming compilation configuration printed at the begining.
# It could be extended, but it is sufficient like this.
# If can be disabled by calling the make using "noinfo".
HEADER_MSG:="COMPILATION OF PROJECT : $(EXEC)"
LINE:=$(shell printf "%$$(tput cols)s" | tr " " "=")
ifeq ($(filter $(MAKECMDGOALS), noinfo),)
build-info:
@printf $(LINE)"\n\e[1m\e[91m$(shell export HEADER_MSG=$(HEADER_MSG); printf " %.0s" $$(seq 1 $$(($$(tput cols)/2 - $${#HEADER_MSG}/2)))) "$(HEADER_MSG)"\e[0m\n\
Generation configuration is \e[1m\e[32m$(EXECUTION) $(CONFIGNAME)\e[0m\n\n\
Sources directory search path is : \e[1m\e[96m$(SRCDIR)\e[0m (SRCDIR)\n\
Includes directory search path is : \e[1m\e[96m$(INCLDIR)\e[0m (INCLDIR)\n\
Libraries directory search path is : \e[1m\e[96m$(LIBDIR)\e[0m (LIBDIR)\n\
Objects directory is : \e[1m\e[96m$(OBJDIR)\e[0m (OBJDIR)\n\
The binary output path is : \e[1m\e[96m$(OUTPATH)\e[0m\n\n\
$(LINE)\n\n"
else
build-info:
endif
SHELL:=/bin/bash
CXX:= clang++
LD:= $(CXX)
JITLD:= llvm-link
EXEC:=$(notdir $(shell pwd))
LIBDIR:= lib
OBJDIR:= obj
SRCDIR:= src
INCLDIR:= include
BINDIR:= bin
SCANDIR:= scan
EXEEXT:=
OBJEXT:=o
DEPEXT:=d
CEXT:=c
CXXEXT:=cxx
STATICLIBEXT:=a
SHAREDLIBEXT:=so
FLAGS:= -W -Wall -Wextra
CFLAGS= $(FLAGS) -std=c11
CXXFLAGS= $(FLAGS) -std=c++1y
DEBUGFLAGS:= -g -O0
RELEASEFLAGS:= -O3
ANALYSISFLAGS:= --analyze -Xanalyzer -analyzer-output=html -o $(SCANDIR)
X86FLAGS:= -m32
X64FLAGS:= -m64
DEPENDFLAGS:= -MMD
LDFLAGS:=
JITFLAGS:= -emit-llvm -S -fno-use-cxa-atexit
STATICLDFLAGS:= rcs
SHAREDFLAGS:= -fPIC
SHAREDLDFLAGS:= -shared
VERBOSEFLAGS:= -v
VERBOSELDFLAGS:= -v
define to_lower
$(strip $(shell echo $1 | tr '[:upper:]' '[:lower:]'))
endef
define to_upper
$(strip $(shell echo $1 | tr '[:lower:]' '[:upper:]'))
endef
define get_flags
$(strip $($(call to_upper, $1)FLAGS))
endef
define get_ldflags
$(strip $($(call to_upper, $1)LDFLAGS))
endef
override ALLEXECUTIONS:=native jit
override DEFAULTEXECUTION:=native
override PASSEDEXECUTION:=$(filter $(MAKECMDGOALS), $(ALLEXECUTIONS))
ifeq ($(words $(PASSEDEXECUTION)), 0)
EXECUTION:=$(DEFAULTEXECUTION)
else ifeq ($(words $(PASSEDEXECUTION)), 1)
EXECUTION:=$(PASSEDEXECUTION)
else
$(error Error : multiple arguments or invalid execution mod !)
endif
FLAGS+=$(call get_flags, $(EXECUTION))
override ALLPLATFORMS:=x86 x64
ifeq ($(shell getconf LONG_BIT), 64)
override DEFAULTPLATFORM:=x64
else ifeq ($(shell getconf LONG_BIT), 32)
override DEFAULTPLATFORM:=x86
endif
override ALLCONFIGS:=debug release analysis
override DEFAULTCONFIG:=debug
override CONFIG_PLATFORM:=$(foreach CONFIG, $(filter-out analysis, $(ALLCONFIGS)),$(addprefix $(CONFIG)-, $(ALLPLATFORMS)))
override CONFIG_PLATFORM+=$(ALLCONFIGS)
override PASSEDCONFIG:=$(filter $(MAKECMDGOALS), $(CONFIG_PLATFORM))
ifeq ($(words $(PASSEDCONFIG)), 0)
CONFIG:=$(DEFAULTCONFIG)
PLATFORM:=$(DEFAULTPLATFORM)
else ifeq ($(words $(PASSEDCONFIG)), 1)
CONFIG:=$(filter $(foreach PLATFORM, $(ALLPLATFORMS),$(patsubst %-$(PLATFORM),%,$(PASSEDCONFIG))), $(ALLCONFIGS))
PLATFORM:=$(filter $(foreach PLATFORM, $(ALLPLATFORMS),$(patsubst %-$(PLATFORM),$(PLATFORM),$(PASSEDCONFIG))), $(ALLPLATFORMS))
else
$(error Error : multiple configuration or invalid configuration mod !)
endif
ifeq ($(PLATFORM),)
ifneq ($(CONFIG), analysis)
PLATFORM:=$(DEFAULTPLATFORM)
endif
endif
FLAGS+=$(call get_flags, $(CONFIG))
private TMP:=$(call get_flags, $(PLATFORM))
FLAGS+=$(TMP)
ifneq ($(EXECUTION), jit)
LDFLAGS+=$(TMP)
endif
override CONFIGNAME:=$(patsubst %-, %, $(CONFIG)-$(PLATFORM))
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 LLVM/clang)
endif
LD= $(JITLD)
EXEC:= $(addprefix $(EXEC).,bc)
OBJEXT:= bco
endif
ifeq ($(CONFIG), analysis)
ifeq (, $(filter $(CXX), clang clang++))
$(error Error : attempt to enable analysis, but not using LLVM/clang)
endif
endif
private override LIBTYPES:=static shared
private override PASSEDLIBTYPE:=$(filter $(MAKECMDGOALS), $(LIBTYPES))
ifneq ($(PASSEDLIBTYPE),)
ifeq ($(EXECUTION), jit)
$(error : Can't build bitcode library ! Please select native execution mode (default))
endif
ifneq ($(words $(PASSEDLIBTYPE)), 1)
$(error : Can't build multiple type of lib at the same time. Please select only one !)
else ifeq ($(PASSEDLIBTYPE), static)
LDFLAGS:=$(filter-out $(call get_flags, $(PLATFORM)), $(LDFLAGS))$(call get_ldflags, static)
LD:=ar
EXEC:=lib$(call to_lower, $(EXEC)).$(STATICLIBEXT)
else ifeq ($(PASSEDLIBTYPE), shared)
FLAGS+=$(call get_flags, shared)
LDFLAGS+=$(call get_ldflags, shared)
EXEC:=lib$(call to_lower, $(EXEC)).$(SHAREDLIBEXT)
OBJEXT:=osh
endif
else ifneq ($(EXEEXT),)
EXEC:=$(EXEC).$(EXEEXT)
endif
SILENT:=@
VERBOSE:=none
ifneq ($(filter $(VERBOSE), some detail all),)
ifneq ($(filter $(VERBOSE), detail all),)
FLAGS+=$(VERBOSEFLAGS)
LDFLAGS+=$(VERBOSELDFLAGS)
ifeq ($(VERBOSE), all)
SHELL_:=$(SHELL)
SHELL=$(warning [$@])$(SHELL_) -x
endif
endif
SILENT:=
endif
SRC:=$(shell find $(SRCDIR) -name '*.$(CEXT)' -o -name '*.$(CXXEXT)')
OBJ:=$(subst src/,,$(SRC:.$(CEXT)=.$(OBJEXT)))
OBJ:=$(subst src/,,$(OBJ:.$(CXXEXT)=.$(OBJEXT)))
OBJS=$(addprefix obj/$(PLATFORM)/$(CONFIG)/, $(OBJ))
DEPS:=$(OBJ:.$(OBJEXT)=.$(DEPEXT))
OUTPATH:=$(if $(filter $(CONFIG), analysis),$(SCANDIR),$(BINDIR)/$(PLATFORM)/$(CONFIG))
.PHONY: clean cleantmp cleanall $(CONFIG_PLATFORM) $(ALLEXECUTIONS)
all: build-info $(OUTPATH)/$(EXEC)
@$(if $(OK),,\
printf "\e[1m\e[32mNothing to do, everything is up to date !\e[0m\n\n")
ifeq ($(filter $(firstword $(MAKECMDGOALS)), clean),)
$(CONFIG_PLATFORM): all
else
$(CONFIG_PLATFORM):
@ printf "Done !"
endif
ifeq ($(filter $(firstword $(MAKECMDGOALS)), $(ALLEXECUTIONS)),)
$(ALLEXECUTIONS):
@#
else
$(ALLEXECUTIONS): $(CONFIGNAME)
endif
ifeq ($(filter $(firstword $(MAKECMDGOALS)), $(LIBTYPES)),)
$(LIBTYPES):
@#
else
$(LIBTYPES): $(CONFIGNAME)
endif
ifeq ($(firstword $(MAKECMDGOALS)), noinfo)
noinfo : $(CONFIGNAME)
else
noinfo:
@#
endif
-include $(addprefix obj/$(PLATFORM)/$(CONFIG)/,$(DEPS))
$(OUTPATH)/$(EXEC) : $(OBJS)
$(eval OK=1)
$(SILENT) mkdir -p $(OUTPATH)
ifeq ($(EXECUTION), jit)
$(SILENT) $(LD) $(LDFLAGS) $^ -o $(OUTPATH)/$(EXEC)
else
$(if $(filter $(CONFIG), analysis),\
@ printf "No executable code output for analysis",\
$(SILENT) printf "Linking binary ... ";\
$(LD) $(LDFLAGS) -o $(OUTPATH)/$(EXEC) $^;\
printf "Done !\n")
endif
@ printf "\e[1m\e[32mGeneration successful !\e[0m\n"
ifeq ($(EXECUTION), native)
$(SILENT) $(if $(filter $(CONFIG), release),\
printf "Stripping binary ... ";\
strip $(OUTPATH)/$(EXEC);\
echo "Done !",)
else
@ printf "\e[1m\e[92mBitcode generated\e[0m\n"
endif
@ printf "Resulting file : \e[1m\e[92m$(EXEC)\e[0m\n\
See the result in the following directory : \e[1m\e[96m$(OUTPATH)\e[0m\n"
$(OBJDIR)/$(PLATFORM)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(@D)
$(SILENT) $(CXX) -I$(INCLDIR) -x c -o $@ -c $< $(CFLAGS) $(DEPENDFLAGS)
$(OBJDIR)/$(PLATFORM)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CXXEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(@D)
$(SILENT) $(CXX) -I$(INCLDIR) -x c++ -o $@ -c $< $(CXXFLAGS) $(DEPENDFLAGS)
.NOTPARALLEL:
clean:
@echo "Cleaning $(CONFIG) $(EXECUTION) temporary objects ..."
$(SILENT) find $(OBJDIR)/$(PLATFORM)/$(CONFIG) -name "*.$(OBJEXT)" -type f -delete -o -name "*.$(DEPEXT)" -type f -delete
cleantmp:
$(SILENT) rm -rf $(OBJDIR)/*
cleanall: cleantmp
$(SILENT) rm -f ./build.gen
$(SILENT) rm -rf $(BINDIR)/*
HEADER_MSG:="COMPILATION OF PROJECT : $(EXEC)"
LINE:=$(shell printf "%$$(tput cols)s" | tr " " "=")
ifeq ($(filter $(MAKECMDGOALS), noinfo),)
build-info:
@printf $(LINE)"\n\e[1m\e[91m$(shell export HEADER_MSG=$(HEADER_MSG); printf " %.0s" $$(seq 1 $$(($$(tput cols)/2 - $${#HEADER_MSG}/2)))) "$(HEADER_MSG)"\e[0m\n\
Generation configuration is \e[1m\e[32m$(EXECUTION) $(CONFIGNAME)\e[0m\n\n\
Sources directory search path is : \e[1m\e[96m$(SRCDIR)\e[0m (SRCDIR)\n\
Includes directory search path is : \e[1m\e[96m$(INCLDIR)\e[0m (INCLDIR)\n\
Libraries directory search path is : \e[1m\e[96m$(LIBDIR)\e[0m (LIBDIR)\n\
Objects directory is : \e[1m\e[96m$(OBJDIR)\e[0m (OBJDIR)\n\
The binary output path is : \e[1m\e[96m$(OUTPATH)\e[0m\n\n\
$(LINE)\n\n"
else
build-info:
endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment