Skip to content

Instantly share code, notes, and snippets.

@Redchards
Last active September 3, 2015 06:12
Show Gist options
  • Save Redchards/8e757bfb77dc4b55e8de to your computer and use it in GitHub Desktop.
Save Redchards/8e757bfb77dc4b55e8de to your computer and use it in GitHub Desktop.
Upgraded and fixed version of the previous makefiles
# This makefile has basically the same goal as the previous ones, but do a greater job.
# Bugs the others had are now fixed ! The compilation for a configuration will not require
# cleaning objs to produce its result.
# PROS : -Easy to use, drop in makefile
# -Quite configurable on its own
# -Manages release and debug mod automatically
# -Supports mix of C and C++ files without further configuration
# CONS : -Bad extensibility
# -Using a sub makefile, so less compact than the previous ones
# -Target dependent variables were not the great idea I thought it would be
# (could be replaced by a filter on MAKECMDGOALS and a list)
# -Some features (like analysis or bitcode compilation) are quite broken.
# The above method shoud some problems they have.
# Default structure of the project should be :
# ./src for the source files (subdirectories are supported)
# ./include for the includes files
# .c for C file names
# .cxx for C++ file names
# So yhea, I still got a bit of work to do on this :)
# 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.
ALLCONFIG=debug release analysis
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
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
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))
export
# .PHONY targets.
.PHONY: clean cleanall debug release analysis
all: build-info $(EXEC)
# Target specific variable value.
# These targets are used to do less typing while invoking the make with a special config.
debug: override CONFIG:=debug
debug: override FLAGS+=$(DEBUGFLAGS)
debug: all
release: override CONFIG:=release
release: override FLAGS+=$(RELEASEFLAGS)
release: all
analysis: override CONFIG:=analysis
analysis: override FLAGS+=$(ANALYZERFLAGS)
analysis: all
# Calling submake which will do the actual compilation based on settings
$(EXEC):
$(SILENT) $(MAKE) -f Makefile.ds --no-print-directory $(BINDIR)/$(CONFIG)/$(EXEC)
# The clean rule is a bit aggressive, and delete everything in obj folders.
# Is it desired behaviour ? To me yes.
clean:
$(SILENT) rm -rf $(OBJDIR)/*
#rm -rf $(OBJDIR)/$(filter $(MAKECMDGOALS), $(ALLCONFIG))/*;
cleanall: clean
$(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.
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
OK=
# This target is the main target.
# It will link all file generated by its dependencies together
-include $(addprefix obj/$(CONFIG)/,$(DEPS))
$(BINDIR)/$(CONFIG)/$(EXEC) : $(OBJS)
ifeq ($(EXECUTION), jit)
$(SILENT) $(LD) $^ -o $(BINDIR)/$(CONFIG)/$(EXEC)
else
$(if $(filter $(CONFIG), analysis),\
@ echo "No executable code output for analysis",\
$(SILENT) echo "Linking binary ...";\
mkdir -p $(BINDIR)/$(CONFIG);\
$(LD) -o $(BINDIR)/$(CONFIG)/$(EXEC) $^ $(LDFLAGS))
endif
@ echo -e "\e[1m\e[32mGeneration successful !\e[0m"
ifeq ($(EXECUTION), native)
$(SILENT) $(if $(filter $(CONFIG), release),\
echo "Stripping binary ...";\
strip $(BINDIR)/$(CONFIG)/$(EXEC);\
echo "Done !",)
else
@ 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",)
# Generation of obj file for C source
$(OBJDIR)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(OBJDIR)/$(CONFIG)/$(dir $*)
$(SILENT) $(CXX) -I$(INCLDIR) -x c -o $(OBJDIR)/$(CONFIG)/$*.$(OBJEXT) -c $< $(CFLAGS) $(MAKEDEPEND)
# Generation of obj file for C++ source
$(OBJDIR)/$(CONFIG)/%.$(OBJEXT): $(SRCDIR)/%.$(CXXEXT)
$(eval OK=1)
$(SILENT) mkdir -p $(OBJDIR)/$(CONFIG)/$(dir $*)
$(SILENT) $(CXX) -I$(INCLDIR) -x c++ -o $(OBJDIR)/$(CONFIG)/$*.$(OBJEXT) -c $< $(CXXFLAGS) $(MAKEDEPEND)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment