Last active
August 29, 2015 14:16
-
-
Save apples/ecbe7de3d68def43e82a to your computer and use it in GitHub Desktop.
Moral of the story: Use Scons.
This file contains 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
# User Configuration | |
# Compiler flags | |
CPPFLAGS += | |
CXXFLAGS += -std=c++1y -pedantic -Wall | |
LDFLAGS += | |
LDLIBS += | |
# Libraries and Executables to be built | |
LIBS += core | |
EXES += client server | |
# Build Modes; First mode is the default | |
MODES := release debug | |
# Mode-specific flags | |
MODES[release].cppflags += -DNDEBUG | |
MODES[release].cxxflags += -O3 | |
MODES[debug].cxxflags += -g -Og | |
# Target-specific flags | |
EXES[client].libdeps += core | |
EXES[server].libdeps += core | |
# Destination directories | |
SRCDIR ?= src | |
BINDIR ?= bin | |
LIBDIR ?= lib | |
INCDIR ?= include | |
# Metadata directories | |
DEPDIR ?= .deps | |
OBJDIR ?= .objs | |
############################### | |
### WARNING WARNING WARNING ### | |
### DO NOT PROCEED ### | |
############################### | |
# All Target | |
.PHONY: all | |
all: | |
# Compiler configuration | |
LibLinkFlag=-l$1 | |
DirLinkFlag=-L$1 | |
DirIncFlag=-I$1 | |
# MakeDirectory(DIR) | |
MakeDirectory=mkdir -p $1 | |
# MakeHeader(SOURCE,HEADER) | |
MakeHeader=cp $1 $2 | |
# MakeDependency(CPPFLAGS,CXXFLAGS,SOURCE,DEP) | |
MakeDependency=$(CXX) $1 $2 -M -MT "`echo $4 | sed 's|$(DEPDIR)/\(.*\).d|$(OBJDIR)/\1.o|'` $4" -MF $4 $3 | |
# MakeObject(CPPFLAGS,CXXFLAGS,SOURCE,OBJ) | |
MakeObject=$(CXX) $1 $2 -c $3 -o $4 | |
# MakeExecutable(LDFLAGS,LDLIBS,OBJS,EXE) | |
MakeExecutable=$(CXX) $1 $3 $2 -o $4 | |
# MakeArchive(OBJS,LIB) | |
MakeArchive=$(AR) rcs $2 $1 | |
CppFlags=$($(CURRENT_TARGET).cppflags) $($(CURRENT_MODE).cppflags) $(CPPFLAGS) | |
CxxFlags=$(CXXFLAGS) $($(CURRENT_MODE).cxxflags) $($(CURRENT_TARGET).cxxflags) | |
LdFlags=$(LDFLAGS) $($(CURRENT_MODE).ldflags) $($(CURRENT_TARGET).ldflags) | |
LdLibs=$($(CURRENT_TARGET).ldlibs) $($(CURRENT_MODE).ldlibs) $(LDLIBS) | |
# Dumb Stuff | |
EMPTY := | |
SPACE :=$(EMPTY) $(EMPTY) | |
TAB = $(SPACE)$(SPACE) | |
# Quiet marker | |
QMARKER:=$(if $(VERBOSE)$(V),,@) | |
# Types | |
define DefClass = | |
$2.SUPER := $1 | |
$2.PROTOTYPE := $3 | |
endef | |
define New = | |
$2.TYPE := $1 | |
$2.REALTYPES := | |
endef | |
IsA=$(findstring $1,$($2.TYPE) $($($2.TYPE).SUPER)) | |
# function InfoVar(VAR) | |
# Equivalent to $(info VAR=$(VAR)). | |
define InfoVar = | |
$$(info $2$1=$$($1)) | |
endef | |
# function DumpInfo(OBJ) | |
# Dumps all known information about the object $(OBJ) with type $($(OBJ).TYPE). | |
define DumpInfo = | |
ifeq ($$(DumpInfo.indent),) | |
$$(eval $$(call InfoVar,$1)) | |
endif | |
DumpInfo.indent := x $$(DumpInfo.indent) | |
DumpInfo.whiteindent = $$(DumpInfo.indent:%=$$$$(SPACE))$$(SPACE) | |
$$(eval $$(call InfoVar,$1.TYPE,$$(DumpInfo.whiteindent))) | |
$$(foreach MEMBER,$$($$($1.TYPE).PROTOTYPE),$$(eval $$(call InfoVar,$1.$$(MEMBER),$$(DumpInfo.whiteindent)))) | |
ifneq ($$(words $$($$($1.TYPE).SUPER)),0) | |
$1.REALTYPES := $$($1.TYPE) $$($1.REALTYPES) | |
$1.TYPE := $$($$($1.TYPE).SUPER) | |
$$(eval $$(call DumpInfo,$1)) | |
$1.TYPE := $$(firstword $$($1.REALTYPES)) | |
$1.REALTYPES := $$(wordlist 2,$$(words $$($1.REALTYPES)),$$($1.REALTYPES)) | |
endif | |
DumpInfo.indent := $$(wordlist 2,$$(words $$(DumpInfo.indent)),$$(DumpInfo.indent)) | |
endef | |
define TypeError = | |
$$(error Cannot call $1 with {$2.TYPE=$$($2.TYPE)}) | |
endef | |
# function AssertType(TYPE,VAR,CALLER) | |
define AssertType = | |
ifeq ($$(call IsA,$1,$2),) | |
$$(eval $$(call TypeError,$3,$2)) | |
endif | |
endef | |
$(eval $(call DefClass,,MODE, \ | |
name \ | |
directory \ | |
cppflags \ | |
cxxflags \ | |
ldflags \ | |
ldlibs \ | |
)) | |
$(eval $(call DefClass,,BIN, \ | |
name \ | |
directory \ | |
cppflags \ | |
cxxflags \ | |
sources \ | |
headers \ | |
deps \ | |
objs \ | |
out \ | |
libdeps \ | |
ignore_subdirs \ | |
)) | |
$(eval $(call DefClass,BIN,LIB, \ | |
headers_out \ | |
)) | |
$(eval $(call DefClass,BIN,EXE, \ | |
ldflags \ | |
ldlibs \ | |
)) | |
# Generated Configuration | |
ALL_DIRS := | |
ALL_DEPS := | |
ALL_OBJS := | |
ALL_LIBS := | |
ALL_EXES := | |
ALL_HEADERS := | |
ifeq ($(OS),Windows_NT) | |
EXESUFF := .exe | |
LIBSUFF := .a | |
else | |
EXESUFF := | |
LIBSUFF := .a | |
endif | |
define FindSources = | |
$$(eval $$(call AssertType,BIN,$1,$0)) | |
$1.sources := $$(if $$(wildcard $$($1.directory)/$$(SRCDIR)),$$(shell find $$($1.directory)/$$(SRCDIR) -name '*.cpp' $$(foreach DIR,$$($1.ignore_subdirs:%=$$($1.directory)/%),-not -path '$$(DIR)*'))) | |
$1.headers := $$(if $$(wildcard $$($1.directory)/$$(INCDIR)),$$(shell find $$($1.directory)/$$(INCDIR) -name '*.hpp' $$(foreach DIR,$$($1.ignore_subdirs:%=$$($1.directory)/%),-not -path '$$(DIR)*'))) | |
endef | |
Modeify=$(foreach MOD,$(MODES:%=MODES[%]),$($1:%=$($(MOD).directory)/%)) | |
define FindOutput = | |
$$(eval $$(call AssertType,BIN,$1,$0)) | |
$1.deps := $$($1.sources:%.cpp=$$(DEPDIR)/%.d) | |
$1.objs := $$($1.sources:%.cpp=$$(OBJDIR)/%.o) | |
ifneq ($$(call IsA,LIB,$1),) | |
$1.out := $$(LIBDIR)/lib$$($1.name)$$(LIBSUFF) | |
$1.headers_out := $$($1.headers:$$($1.directory)/%=%) | |
else ifneq ($$(call IsA,EXE,$1),) | |
$1.out := $$(BINDIR)/$$($1.name)$$(EXESUFF) | |
endif | |
endef | |
GetAllLibdeps=$($1.libdeps) $(foreach lib,$($1.libdeps),$(call GetAllLibdeps,LIBS[$(lib)])) | |
define NormalizeLibdeps = | |
$$(eval $$(call AssertType,EXE,$1,$0)) | |
$1.libdeps := $(call GetAllLibdeps,$1) | |
endef | |
# Madness Ensues | |
define CreateModeData = | |
$$(eval $$(call New,MODE,MODES[$1])) | |
MODES[$1].name := $1 | |
MODES[$1].directory ?= $1 | |
MODES[$1].cppflags += $$(call DirIncFlag,$$(MODES[$1].directory)/$$(INCDIR)) | |
MODES[$1].ldflags += $$(call DirLinkFlag,$$(MODES[$1].directory)/$$(LIBDIR)) | |
MODES[$1].objs := | |
MODES[$1].deps := | |
MODES[$1].headers_out := | |
MODES[$1].outs := | |
.PHONY: MODES[$1] | |
MODES[$1]: | |
all: MODES[$1] | |
endef | |
# function CreateTargetData(TYPE,NAME) | |
# Constructs a target object named $(TYPE).$(NAME) and packs it with data. | |
define CreateTargetData = | |
$$(eval $$(call New,$(1:%S=%),$1[$2])) | |
$1[$2].name := $2 | |
$1[$2].directory ?= $2 | |
$1[$2].cppflags += $$(call DirIncFlag,$2/include) | |
$$(eval $$(call FindSources,$1[$2])) | |
$$(eval $$(call FindOutput,$1[$2])) | |
ifneq ($$(call IsA,EXE,$1[$2]),) | |
$$(eval $$(call NormalizeLibdeps,$1[$2])) | |
$1[$2].ldlibs += $$(foreach LIB,$$($1[$2].libdeps),$$(call LibLinkFlag,$$(LIB))) | |
endif | |
endef | |
ImbueMode=$(foreach F,$1,$($2.directory)/$(F)) | |
define MakeSourceRule = | |
MY_DEP := $$(call ImbueMode,$$(DEPDIR)/$(1:%.cpp=%.d),$2) | |
MY_OBJ := $$(call ImbueMode,$$(OBJDIR)/$(1:%.cpp=%.o),$2) | |
$$(MY_DEP) $$(MY_OBJ): private CURRENT_SOURCE := $1 | |
$$(MY_OBJ): $$(MY_DEP) | |
$$(MY_DEP): | $$(dir $$(MY_DEP)) | |
$$(MY_OBJ): | $$(dir $$(MY_OBJ)) | |
endef | |
define MakeHeaderRule = | |
MY_HEADER := $$(call ImbueMode,$1,$2) | |
$$(MY_HEADER): private CURRENT_SOURCE := $$($3.directory)/$1 | |
$$(MY_HEADER): | $$(dir $$(MY_HEADER)) | |
endef | |
# function SetupTarget(TARGET,MODE) | |
define SetupTarget = | |
$$(eval $$(call AssertType,BIN,$1,$0)) | |
$$(eval $$(call AssertType,MODE,$2,$0)) | |
MY_OUT := $$(call ImbueMode,$$($1.out),$2) | |
MY_OBJS := $$(call ImbueMode,$$($1.objs),$2) | |
MY_DEPS := $$(call ImbueMode,$$($1.deps),$2) | |
MY_HEADERS := | |
ifneq ($$(call IsA,LIB,$1),) | |
MY_HEADERS := $$(call ImbueMode,$$($1.headers_out),$2) | |
endif | |
MY_LIBDEPS_HEADERS := $$(foreach LIBDEP,$$($1.libdeps),$$(call ImbueMode,$$($$(LIBDEP:%=LIBS[%]).headers_out),$2)) | |
MY_LIBDEPS_OUTS := $$(foreach LIBDEP,$$($1.libdeps),$$(call ImbueMode,$$($$(LIBDEP:%=LIBS[%]).out),$2)) | |
$$(MY_OUT) $$(MY_OBJS) $$(MY_DEPS) $$(MY_HEADERS): private CURRENT_TARGET := $1 | |
$$(MY_OUT) $$(MY_OBJS) $$(MY_DEPS) $$(MY_HEADERS): private CURRENT_MODE := $2 | |
$$(MY_DEPS): | $$(MY_LIBDEPS_HEADERS) | |
ifneq ($$(MAKECMDGOALS),clean) | |
-include $$(MY_DEPS) | |
endif | |
$$(MY_OUT): private CURRENT_OBJS := $$(MY_OBJS) | |
$$(MY_OUT): $$(MY_OBJS) $$(MY_LIBDEPS_OUTS) | $$(dir $$(MY_OUT)) $$(MY_HEADERS) | |
$$(foreach SRC,$$($1.sources),$$(eval $$(call MakeSourceRule,$$(SRC),$2))) | |
$$(foreach HEADER,$$($1.headers_out),$$(eval $$(call MakeHeaderRule,$$(HEADER),$2,$1))) | |
$2: $$(MY_OUT) | |
ALL_DEPS += $$(MY_DEPS) | |
ALL_OBJS += $$(MY_OBJS) | |
ifneq ($$(call IsA,LIB,$1),) | |
ALL_HEADERS += $$(MY_HEADERS) | |
ALL_LIBS += $$(MY_OUT) | |
else ifneq ($$(call IsA,EXE,$1),) | |
ALL_EXES += $$(MY_OUT) | |
endif | |
endef | |
$(foreach MOD,$(MODES), \ | |
$(eval $(call CreateModeData,$(MOD))) \ | |
) | |
$(foreach LIB,$(LIBS), \ | |
$(eval $(call CreateTargetData,LIBS,$(LIB))) \ | |
) | |
$(foreach EXE,$(EXES), \ | |
$(eval $(call CreateTargetData,EXES,$(EXE))) \ | |
) | |
$(foreach BIN,$(LIBS:%=LIBS[%]) $(EXES:%=EXES[%]), \ | |
$(foreach MOD,$(MODES:%=MODES[%]), \ | |
$(eval $(call SetupTarget,$(BIN),$(MOD))) \ | |
) \ | |
) | |
ALL_DIRS := $(sort $(dir $(ALL_DEPS) $(ALL_OBJS) $(ALL_HEADERS) $(ALL_LIBS) $(ALL_EXES))) | |
$(ALL_DIRS): | |
$(info [DIR] $@) | |
$(QMARKER)$(call MakeDirectory,$@) | |
$(ALL_DEPS): | |
$(info [DEP] $@) | |
$(QMARKER)$(call MakeDependency,$(call CppFlags),$(call CxxFlags),$(CURRENT_SOURCE),$@) | |
$(ALL_OBJS): | |
$(info [OBJ] $@) | |
$(QMARKER)$(call MakeObject,$(call CppFlags),$(call CxxFlags),$(CURRENT_SOURCE),$@) | |
$(ALL_HEADERS): | |
$(info [HPP] $@) | |
$(QMARKER)$(call MakeHeader,$(CURRENT_SOURCE),$@) | |
$(ALL_LIBS): | |
$(info [LIB] $@) | |
$(QMARKER)$(call MakeArchive,$(CURRENT_OBJS),$@) | |
$(ALL_EXES): | |
$(info [EXE] $@) | |
$(QMARKER)$(call MakeExecutable,$(call LdFlags),$(call LdLibs),$(CURRENT_OBJS),$@) | |
.PHONY: clean | |
clean: | |
rm -f $(ALL_DEPS) $(ALL_OBJS) $(ALL_LIBS) $(ALL_EXES) $(ALL_HEADERS) | |
-find $(foreach MOD,$(MODES:%=MODES[%]),$($(MOD).directory)) -depth -type d -exec rmdir {} \; | |
.PHONY: dumpvars | |
dumpvars: | |
$(eval $(call InfoVar,ALL_DIRS)) | |
$(eval $(call InfoVar,ALL_DEPS)) | |
$(eval $(call InfoVar,ALL_OBJS)) | |
$(eval $(call InfoVar,ALL_LIBS)) | |
$(eval $(call InfoVar,ALL_EXES)) | |
$(eval $(call InfoVar,ALL_HEADERS)) | |
$(foreach LIB,$(LIBS),$(eval $(call DumpInfo,LIBS[$(LIB)]))) | |
$(foreach EXE,$(EXES),$(eval $(call DumpInfo,EXES[$(EXE)]))) | |
$(foreach MOD,$(MODES:%=MODES[%]),$(eval $(call DumpInfo,$(MOD)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment