diff --git a/makefile.basic b/makefile.basic
index bf81725..9caa392 100644
--- a/makefile.basic
+++ b/makefile.basic
@@ -23,19 +23,16 @@ SOURCE_DIRS::=src
SOURCES::=
OBJDIR::=obj
DEPDIR::=$(OBJDIR)/dep
-LIBDIRS::=lib
+LIBDIRS::=
INCLUDE_DIRS::=include
CFLAGS::=-std=c18 -Wall -pedantic -Wextra
CXXFLAGS::=-std=c++17 -Wall -pedantic -Wextra
EXT::=cpp
LANG::=$(EXT)
MAIN_EXECUTABLE::=tester
-PRE_TARGETS::=
-POST_TARGETS::=
-CLEAN_TARGETS::=
RELEASE?=0
MEMCHK?=0
-SAVECFLAGS?=1
+SAVEFLAGS?=1
ifneq ($(WINDOWS),1)
#*nix settings
@@ -59,23 +56,29 @@ else #windows
RANLIB::=$(MINGW_PREFIX)ranlib
AR::=$(MINGW_PREFIX)ar
AS::=$(MINGW_PREFIX)as
+ MAIN_EXECUTABLE::=$(MAIN_EXECUTABLE).exe
endif #windows
-#Put your custom targets for PRE_TARGETS, POST_TARGETS, and CLEAN_TARGETS here:
+#main targets
+.PHONY: all
+#prerun targets
+all::
+#main target
+all:: | flags-update $(MAIN_EXECUTABLE)
+#postrun targets
+all::
+
+#custom clean targets
+clean::
###########################################################################################################
#Everything past this point is internal BS, probably best not to touch it unless you know what you're doing
-#set the all target as the default target, otherwise the topmost target will run
+#set the default target as the default target, otherwise the topmost target will run
.DEFAULT_GOAL::=all
-#set the main target to match the output of mingw
-ifeq ($(WINDOWS),1)
- MAIN_EXECUTABLE::=$(MAIN_EXECUTABLE).exe
-endif
-
#system dependant bullshit
ifeq ($(OS),Windows_NT)
#windows' cmd commands
@@ -102,10 +105,11 @@ else ifeq ($(LANG),c)
COMPILER::=$(CC)
endif
+
ifeq ($(RELEASE),1)
- #a lot of false strict aliasing warnings from gcc 9
- COMPILER_FLAGS+=-O2 -Wno-strict-aliasing
- POST_TARGETS+= do_strip
+#a lot of false strict aliasing warnings from gcc 9
+COMPILER_FLAGS+=-O2 -Wno-strict-aliasing
+
else ifeq ($(MEMCHK),1)
#use asan to check memory leaks/invalid accesses
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
@@ -115,22 +119,22 @@ else
COMPILER_FLAGS+=-O0 -g3 -ggdb
endif
-ifeq ($(SAVECFLAGS),1)
+ifeq ($(SAVEFLAGS),1)
CFLAGS_TMPFILE::=.cflags.tmp
+ LDFLAGS_TMPFILE::=.ldflags.tmp
OLD_COMPILEFLAGS::=$(file <$(CFLAGS_TMPFILE))
+ OLD_LINKFLAGS::=$(file <$(LDFLAGS_TMPFILE))
endif
+
#add dependency tracking and include directories
INTERNAL_COMPILERFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))"
INTERNAL_LINKFLAGS=$(foreach dir,$(LIBDIRS),-L"$(dir)")
-THIS_MAKEFILE_NAME::=$(lastword $(MAKEFILE_LIST))
INTERNAL_SOURCES::=$(SOURCES) $(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext))))
OBJECTS::=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
ALL_COMPILEFLAGS=$(COMPILER_FLAGS) $(INTERNAL_COMPILERFLAGS)
ALL_LINKFLAGS=$(INTERNAL_LINKFLAGS) $(LDFLAGS)
-#Arguments to make submake use this makefile without "Entering directory" stuff
-SUBMAKE_ARGUMENTS::=--no-print-directory -e -f "$(THIS_MAKEFILE_NAME)"
#just a variable for a newline
define \n
@@ -139,40 +143,39 @@ endef
.PHONY: cflags-update
cflags-update:
-ifeq ($(SAVECFLAGS),1)
+ifeq ($(SAVEFLAGS),1)
ifneq ($(subst -MF"$(DEPDIR)/",-MF"$(DEPDIR)/cflags-update",$(ALL_COMPILEFLAGS)),$(OLD_COMPILEFLAGS))
$(file >$(CFLAGS_TMPFILE),$(ALL_COMPILEFLAGS))
endif
+$(CFLAGS_TMPFILE): cflags-update
+endif
+.PHONY: ldflags-update
+ldflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(ALL_LINKFLAGS),$(OLD_LINKFLAGS))
+ $(file >$(LDFLAGS_TMPFILE),$(ALL_LINKFLAGS))
+endif
+$(LDFLAGS_TMPFILE): ldflags-update
endif
-#default target: run targets in PRE_TARGETS, then the main executable, then POST_TARGETS
-.PHONY: all
-all: cflags-update
- $(foreach target,$(PRE_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
- @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(MAIN_EXECUTABLE)"
- $(foreach target,$(POST_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
-#Called in POST_TARGETS when RELEASE=1
-.PHONY: do_strip
-do_strip:
- $(STRIP) --strip-all "$(MAIN_EXECUTABLE)"
+.PHONY: flags-update
+flags-update: cflags-update ldflags-update
#Link executable
-$(MAIN_EXECUTABLE): $(OBJECTS)
- $(COMPILER) $^ -o "$(basename $@)" $(ALL_LINKFLAGS) $(LDLIBS)
+$(MAIN_EXECUTABLE): $(OBJECTS) $(LDFLAGS_TMPFILE)
+ $(COMPILER) $(OBJECTS) -o "$(basename $@)" $(ALL_LINKFLAGS) $(LDLIBS)
+ifeq ($(RELEASE),1)
+ $(STRIP) --strip-all "$(MAIN_EXECUTABLE)"
+endif
#Object target recipe
-define GENERATE_OBJECTS
-$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).%.o: $(1)/% $(CFLAGS_TMPFILE)
- $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
-endef
define GENERATE_INDIVIDUAL_OBJECTS
$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
$$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
endef
#Create targets for object files
-$(foreach dir,$(SOURCE_DIRS),$(eval $(call GENERATE_OBJECTS,$(dir))))
-$(foreach src,$(SOURCES),$(eval $(call GENERATE_INDIVIDUAL_OBJECTS,$(src))))
+$(foreach src,$(INTERNAL_SOURCES),$(eval $(call GENERATE_INDIVIDUAL_OBJECTS,$(src))))
$(OBJECTS): | $(OBJDIR) $(DEPDIR)
#Output directory creation
@@ -182,14 +185,14 @@ $(DEPDIR):
$(call mkdir,"$@")
.PHONY: clean
-clean:
- $(foreach target,$(CLEAN_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+clean::
$(call rmdir,"$(DEPDIR)")
$(call rmdir,"$(OBJDIR)")
$(call rm,"$(MAIN_EXECUTABLE)")
$(call rm,"$(MAIN_EXECUTABLE).exe")
-ifeq ($(SAVECFLAGS),1)
+ifeq ($(SAVEFLAGS),1)
$(call rm,"$(CFLAGS_TMPFILE)")
+ $(call rm,"$(LDFLAGS_TMPFILE)")
endif
#header file dep tracking
diff --git a/makefile.library b/makefile.library
index 3ef1ed2..53a6a3f 100644
--- a/makefile.library
+++ b/makefile.library
@@ -30,14 +30,11 @@ CXXFLAGS::=-std=c++17 -Wall -pedantic -Wextra
EXT::=cpp
LANG::=$(EXT)
MAIN_LIBRARY::=tester
-PRE_TARGETS::=
-POST_TARGETS::=
-CLEAN_TARGETS::=
SHARED?=1
STATIC?=0
RELEASE?=0
MEMCHK?=0
-SAVECFLAGS?=1
+SAVEFLAGS?=1
ifneq ($(WINDOWS),1)
#*nix settings
@@ -49,6 +46,9 @@ ifneq ($(WINDOWS),1)
RANLIB::=ranlib
AR::=ar
AS::=as
+ ifeq ($(SHARED),1)
+ INTERNAL_SHARED_LIBRARY::=lib$(MAIN_LIBRARY).so
+ endif
else #windows
#windows settings
#windows is a fuckwit
@@ -61,11 +61,29 @@ else #windows
RANLIB::=$(MINGW_PREFIX)ranlib
AR::=$(MINGW_PREFIX)ar
AS::=$(MINGW_PREFIX)as
+ ifeq ($(SHARED),1)
+ INTERNAL_SHARED_LIBRARY::=$(MAIN_LIBRARY).dll
+ endif
endif #windows
+ifeq ($(STATIC),1)
+ INTERNAL_STATIC_LIBRARY::=lib$(MAIN_LIBRARY).a
+endif
#Put your custom targets for PRE_TARGETS and POST_TARGETS here:
+#default target
+.PHONY: all
+#pre targets
+all::
+
+all:: flags-update $(INTERNAL_STATIC_LIBRARY) $(INTERNAL_SHARED_LIBRARY)
+
+#post targets
+all::
+
+#custom clean targets
+clean::
###########################################################################################################
#Everything past this point is internal BS, probably best not to touch it unless you know what you're doing
@@ -74,16 +92,6 @@ endif #windows
.DEFAULT_GOAL::=all
#setup the actual output library name depending on shared/static and windows/anything else
-ifeq ($(WINDOWS),1)
- INTERNAL_SHARED_LIBRARY::=lib$(MAIN_LIBRARY).a
-else
- INTERNAL_SHARED_LIBRARY::=lib$(MAIN_LIBRARY).so
-endif
-SHARED_LIBRARY_FLAGS=-fPIC
-DLLOUT::=$(MAIN_LIBRARY).dll
-INTERNAL_STATIC_LIBRARY::=lib$(MAIN_LIBRARY).a
-
-
#system dependant bullshit
ifeq ($(OS),Windows_NT)
#windows' cmd commands
@@ -113,7 +121,6 @@ endif
ifeq ($(RELEASE),1)
#a lot of false strict aliasing warnings from gcc 9
COMPILER_FLAGS+=-O2 -Wno-strict-aliasing
- POST_TARGETS+= do_strip
else ifeq ($(MEMCHK),1)
#use asan to check memory leaks/invalid accesses
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
@@ -123,118 +130,101 @@ else
COMPILER_FLAGS+=-O0 -g3 -ggdb
endif
-ifeq ($(SAVECFLAGS),1)
+ifeq ($(SAVEFLAGS),1)
CFLAGS_TMPFILE::=.cflags.tmp
+ LDFLAGS_TMPFILE::=.ldflags.tmp
OLD_COMPILEFLAGS::=$(file <$(CFLAGS_TMPFILE))
+ OLD_LINKFLAGS::=$(file <$(LDFLAGS_TMPFILE))
endif
#add dependency tracking and include directories
INTERNAL_COMPILERFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))"
+ifeq ($(WINDOWS),1)
+INTERNAL_LINKFLAGS=-Wl,--out-implib,"lib$(MAIN_LIBRARY).a" $(foreach dir,$(LIBDIRS),-L"$(dir)")
+else
INTERNAL_LINKFLAGS=$(foreach dir,$(LIBDIRS),-L"$(dir)")
-ifeq ($(SHARED),1)
- INTERNAL_COMPILERFLAGS+=$(SHARED_LIBRARY_FLAGS)
endif
-THIS_MAKEFILE_NAME::=$(lastword $(MAKEFILE_LIST))
INTERNAL_SOURCES::=$(SOURCES) $(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext))))
-OBJECTS::=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
+STATIC_OBJECTS::=$(addprefix $(OBJDIR)/static/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
+SHARED_OBJECTS::=$(addprefix $(OBJDIR)/shared/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
ALL_COMPILEFLAGS=$(COMPILER_FLAGS) $(INTERNAL_COMPILERFLAGS)
ALL_LINKFLAGS=$(INTERNAL_LINKFLAGS) $(LDFLAGS)
-#Arguments to make submake use this makefile without "Entering directory" stuff
-SUBMAKE_ARGUMENTS::=--no-print-directory -e -f "$(THIS_MAKEFILE_NAME)"
-#just a variable for a newline
-define \n
-
-
-endef
-
.PHONY: cflags-update
cflags-update:
-ifeq ($(SAVECFLAGS),1)
+ifeq ($(SAVEFLAGS),1)
ifneq ($(subst -MF"$(DEPDIR)/",-MF"$(DEPDIR)/cflags-update",$(ALL_COMPILEFLAGS)),$(OLD_COMPILEFLAGS))
$(file >$(CFLAGS_TMPFILE),$(ALL_COMPILEFLAGS))
endif
+$(CFLAGS_TMPFILE): cflags-update
endif
-#default target: run targets in PRE_TARGETS, then build the main library, then POST_TARGETS
-.PHONY: all
-all: cflags-update
- $(foreach target,$(PRE_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
-ifeq ($(SHARED),1)
-ifeq ($(WINDOWS),1)
- @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(DLLOUT)"
-else #windows
- @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(INTERNAL_SHARED_LIBRARY)"
-endif #windows
-endif #shared
-ifeq ($(STATIC),1)
-ifeq ($(SHARED),1)
- @$(MAKE) $(SUBMAKE_ARGUMENTS) "object_clean"
-endif #shared
- @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(INTERNAL_STATIC_LIBRARY)" SHARED=0
-endif #static
- $(foreach target,$(POST_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+.PHONY: ldflags-update
+ldflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(ALL_LINKFLAGS),$(OLD_LINKFLAGS))
+ $(file >$(LDFLAGS_TMPFILE),$(ALL_LINKFLAGS))
+endif
+$(LDFLAGS_TMPFILE): ldflags-update
+endif
+.PHONY: flags-update
+flags-update: cflags-update ldflags-update
-ifeq ($(WINDOWS),1)
-#target for windows shared library
-$(DLLOUT): $(OBJECTS)
- $(COMPILER) -shared -o "$(DLLOUT)" $^ -Wl,--out-implib,"lib$(MAIN_LIBRARY).a" $(SHARED_LIBRARY_FLAGS) $(ALL_LINKFLAGS) $(LDLIBS)
-else #windows
-#target for *nix shared library
-$(INTERNAL_SHARED_LIBRARY): $(OBJECTS)
- $(COMPILER) -shared -o "$@" $^ $(COMPILER_FLAGS) $(SHARED_LIBRARY_FLAGS) $(ALL_LINKFLAGS) $(LDLIBS)
-endif #windows
+#target for shared library
+$(INTERNAL_SHARED_LIBRARY): $(SHARED_OBJECTS) $(LDFLAGS_TMPFILE)
+ $(COMPILER) -shared -o "$@" $(SHARED_OBJECTS) -fPIC $(ALL_LINKFLAGS) $(LDLIBS)
+ifeq ($(RELEASE),1)
+ $(STRIP) --strip-debug "$@"
+endif
#target for static library
-$(INTERNAL_STATIC_LIBRARY): $(OBJECTS)
+$(INTERNAL_STATIC_LIBRARY): $(STATIC_OBJECTS)
$(AR) rcs "$@" $^
$(RANLIB) "$@"
-#Called in POST_TARGETS when RELEASE=1
-.PHONY: do_strip
-do_strip:
-ifeq ($(SHARED),1)
- $(STRIP) --strip-debug "$(INTERNAL_SHARED_LIBRARY)"
-endif
-ifeq ($(STATIC),1)
- $(STRIP) --strip-debug "$(INTERNAL_STATIC_LIBRARY)"
-endif
-
#Object target recipe
-define GENERATE_OBJECTS
-$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).%.o: $(1)/% $(CFLAGS_TMPFILE)
- $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
+define GENERATE_SHARED_OBJECTS
+$$(OBJDIR)/shared/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
+ $$(COMPILER) $$(ALL_COMPILEFLAGS) -fPIC "$$<" -o "$$@"
endef
-define GENERATE_INDIVIDUAL_OBJECTS
-$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
+#Object target recipe
+define GENERATE_STATIC_OBJECTS
+$$(OBJDIR)/static/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
$$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
endef
#Create targets for object files
-$(foreach dir,$(SOURCE_DIRS),$(eval $(call GENERATE_OBJECTS,$(dir))))
-$(foreach src,$(SOURCES),$(eval $(call GENERATE_INDIVIDUAL_OBJECTS,$(src))))
-$(OBJECTS): | $(OBJDIR) $(DEPDIR)
+ifeq ($(SHARED),1)
+$(foreach src,$(INTERNAL_SOURCES),$(eval $(call GENERATE_SHARED_OBJECTS,$(src))))
+endif
+ifeq ($(STATIC),1)
+$(foreach src,$(INTERNAL_SOURCES),$(eval $(call GENERATE_STATIC_OBJECTS,$(src))))
+endif
+
+$(STATIC_OBJECTS): | $(OBJDIR)/static $(DEPDIR)
+$(SHARED_OBJECTS): | $(OBJDIR)/shared $(DEPDIR)
$(OBJDIR):
$(call mkdir,"$@")
+$(OBJDIR)/static: $(OBJDIR)
+ $(call mkdir,"$@")
+$(OBJDIR)/shared: $(OBJDIR)
+ $(call mkdir,"$@")
$(DEPDIR):
$(call mkdir,"$@")
-.PHONY: object_clean
-object_clean:
- $(call rmdir,"$(OBJDIR)")
-
.PHONY: clean
-clean: object_clean
- $(foreach target,$(CLEAN_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+clean::
+ $(call rmdir,"$(OBJDIR)")
$(call rmdir,"$(DEPDIR)")
$(call rm,"lib$(MAIN_LIBRARY).so")
$(call rm,"lib$(MAIN_LIBRARY).a")
- $(call rm,"$(DLLOUT)")
-ifeq ($(SAVECFLAGS),1)
+ $(call rm,"$(MAIN_LIBRARY).dll")
+ifeq ($(SAVEFLAGS),1)
$(call rm,"$(CFLAGS_TMPFILE)")
+ $(call rm,"$(LDFLAGS_TMPFILE)")
endif
#header file dep tracking
diff --git a/makefile.reclib b/makefile.reclib
new file mode 100644
index 0000000..76fc10e
--- /dev/null
+++ b/makefile.reclib
@@ -0,0 +1,264 @@
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see .
+
+#Copyright 2018-2020 rexy712
+
+#Makefile to generate a single static or shared library from all the sources in SOURCE_DIRS ending in EXT
+
+ifeq ($(OS),Windows_NT)
+ WINDOWS::=1
+endif
+
+SOURCE_DIRS::=src
+SOURCES::=
+OBJDIR::=obj
+DEPDIR::=$(OBJDIR)/dep
+LIBDIRS::=lib
+INCLUDE_DIRS::=include
+CFLAGS::=-std=c18 -Wall -pedantic -Wextra
+CXXFLAGS::=-std=c++17 -Wall -pedantic -Wextra
+EXT::=cpp
+LANG::=$(EXT)
+MAIN_LIBRARY::=tester
+PRE_TARGETS::=
+POST_TARGETS::=
+CLEAN_TARGETS::=
+SHARED?=1
+STATIC?=0
+RELEASE?=0
+MEMCHK?=0
+SAVEFLAGS?=1
+
+ifneq ($(WINDOWS),1)
+ #*nix settings
+ CC::=gcc
+ CXX::=g++
+ LDLIBS::=
+ LDFLAGS::=
+ STRIP::=strip
+ RANLIB::=ranlib
+ AR::=ar
+ AS::=as
+else #windows
+ #windows settings
+ #windows is a fuckwit
+ MINGW_PREFIX::=x86_64-w64-mingw32-
+ CC::=$(MINGW_PREFIX)gcc
+ CXX::=$(MINGW_PREFIX)g++
+ LDLIBS::=
+ LDFLAGS::=-static-libgcc -static-libstdc++
+ STRIP::=$(MINGW_PREFIX)strip
+ RANLIB::=$(MINGW_PREFIX)ranlib
+ AR::=$(MINGW_PREFIX)ar
+ AS::=$(MINGW_PREFIX)as
+endif #windows
+
+#Put your custom targets for PRE_TARGETS and POST_TARGETS here:
+
+
+
+###########################################################################################################
+#Everything past this point is internal BS, probably best not to touch it unless you know what you're doing
+
+#set the all target as the default target, otherwise the topmost target will run
+.DEFAULT_GOAL::=all
+
+#setup the actual output library name depending on shared/static and windows/anything else
+ifeq ($(WINDOWS),1)
+ INTERNAL_SHARED_LIBRARY::=lib$(MAIN_LIBRARY).a
+else
+ INTERNAL_SHARED_LIBRARY::=lib$(MAIN_LIBRARY).so
+endif
+SHARED_LIBRARY_FLAGS=-fPIC
+DLLOUT::=$(MAIN_LIBRARY).dll
+INTERNAL_STATIC_LIBRARY::=lib$(MAIN_LIBRARY).a
+
+
+#system dependant bullshit
+ifeq ($(OS),Windows_NT)
+ #windows' cmd commands
+ mkdir=mkdir $(subst /,\,$(1)) > NUL 2>&1
+ rm=del /F $(1) > NUL 2>&1
+ rmdir=rd /S /Q $(1) > NUL 2>&1
+ move=move /Y $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
+ copy=copy /Y /B $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
+else
+ #*nix terminal commands
+ mkdir=mkdir -p $(1)
+ rm=rm -f $(1)
+ rmdir=rm -rf $(1)
+ move=mv $(1) $(2)
+ copy=cp $(1) $(2)
+endif
+
+#setup compiler and flags based on language
+ifeq ($(LANG),cpp)
+ COMPILER_FLAGS::=$(CXXFLAGS)
+ COMPILER::=$(CXX)
+else ifeq ($(LANG),c)
+ COMPILER_FLAGS::=$(CFLAGS)
+ COMPILER::=$(CC)
+endif
+
+ifeq ($(RELEASE),1)
+ #a lot of false strict aliasing warnings from gcc 9
+ COMPILER_FLAGS+=-O2 -Wno-strict-aliasing
+ POST_TARGETS+= do_strip
+else ifeq ($(MEMCHK),1)
+ #use asan to check memory leaks/invalid accesses
+ LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
+ COMPILER_FLAGS+=-O0 -g3 -ggdb -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
+else
+ #default target
+ COMPILER_FLAGS+=-O0 -g3 -ggdb
+endif
+
+ifeq ($(SAVEFLAGS),1)
+ CFLAGS_TMPFILE::=.cflags.tmp
+ OLD_COMPILEFLAGS::=$(file <$(CFLAGS_TMPFILE))
+ LDFLAGS_TMPFILE::=.ldflags.tmp
+ OLD_LINKFLAGS::=$(file <$(LDFLAGS_TMPFILE))
+endif
+
+#add dependency tracking and include directories
+INTERNAL_COMPILERFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))"
+INTERNAL_LINKFLAGS=$(foreach dir,$(LIBDIRS),-L"$(dir)")
+ifeq ($(SHARED),1)
+ INTERNAL_COMPILERFLAGS+=$(SHARED_LIBRARY_FLAGS)
+endif
+THIS_MAKEFILE_NAME::=$(lastword $(MAKEFILE_LIST))
+INTERNAL_SOURCES::=$(SOURCES) $(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext))))
+ifeq ($(STATIC),1)
+STATIC_OBJECTS::=$(addprefix $(OBJDIR)/static/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
+endif
+ifeq ($(SHARED),1)
+SHARED_OBJECTS::=$(addprefix $(OBJDIR)/shared/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
+endif
+ALL_COMPILEFLAGS=$(COMPILER_FLAGS) $(INTERNAL_COMPILERFLAGS)
+ALL_LINKFLAGS=$(INTERNAL_LINKFLAGS) $(LDFLAGS)
+
+#Arguments to make submake use this makefile without "Entering directory" stuff
+SUBMAKE_ARGUMENTS::=--no-print-directory -e -f "$(THIS_MAKEFILE_NAME)"
+#just a variable for a newline
+define \n
+
+
+endef
+
+.PHONY: cflags-update
+cflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(subst -MF"$(DEPDIR)/",-MF"$(DEPDIR)/cflags-update",$(ALL_COMPILEFLAGS)),$(OLD_COMPILEFLAGS))
+ $(file >$(CFLAGS_TMPFILE),$(ALL_COMPILEFLAGS))
+endif
+endif
+.PHONY: ldflags-update
+ldflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(ALL_LINKFLAGS),$(OLD_LINKFLAGS))
+ $(file >$(LDFLAGS_TMPFILE),$(ALL_LINKFLAGS))
+endif
+endif
+.PHONY: flags-update
+flags-update: cflags-update ldflags-update
+
+#default target: run targets in PRE_TARGETS, then build the main library, then POST_TARGETS
+.PHONY: all
+all: flags-update
+ $(foreach target,$(PRE_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+ifeq ($(SHARED),1)
+ifeq ($(WINDOWS),1)
+ @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(DLLOUT)" STATIC=0
+else #windows
+ @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(INTERNAL_SHARED_LIBRARY)" STATIC=0
+endif #windows
+endif #shared
+ifeq ($(STATIC),1)
+ @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(INTERNAL_STATIC_LIBRARY)" SHARED=0
+endif #static
+ $(foreach target,$(POST_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+
+ifeq ($(WINDOWS),1)
+#target for windows shared library
+$(DLLOUT): $(SHARED_OBJECTS) $(LDFLAGS_TMPFILE)
+ $(COMPILER) -shared -o "$(DLLOUT)" $(SHARED_OBJECTS) -Wl,--out-implib,"lib$(MAIN_LIBRARY).a" $(SHARED_LIBRARY_FLAGS) $(ALL_LINKFLAGS) $(LDLIBS)
+else #windows
+
+#target for *nix shared library
+$(INTERNAL_SHARED_LIBRARY): $(SHARED_OBJECTS) $(LDFLAGS_TMPFILE)
+ $(COMPILER) -shared -o "$@" $(SHARED_OBJECTS) $(SHARED_LIBRARY_FLAGS) $(ALL_LINKFLAGS) $(LDLIBS)
+endif #windows
+
+#target for static library
+$(INTERNAL_STATIC_LIBRARY): $(STATIC_OBJECTS)
+ $(AR) rcs "$@" $^
+ $(RANLIB) "$@"
+
+
+#Called in POST_TARGETS when RELEASE=1
+.PHONY: do_strip
+do_strip:
+ifeq ($(SHARED),1)
+ $(STRIP) --strip-debug "$(INTERNAL_SHARED_LIBRARY)"
+endif
+ifeq ($(STATIC),1)
+ $(STRIP) --strip-debug "$(INTERNAL_STATIC_LIBRARY)"
+endif
+
+#Object target recipe
+define GENERATE_STATIC_OBJECTS
+$$(OBJDIR)/static/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
+ $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
+endef
+define GENERATE_SHARED_OBJECTS
+$$(OBJDIR)/shared/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
+ $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
+endef
+
+
+#Create targets for object files
+ifeq ($(STATIC),1)
+$(foreach src,$(INTERNAL_SOURCES),$(eval $(call GENERATE_STATIC_OBJECTS,$(src))))
+endif
+ifeq ($(SHARED),1)
+$(foreach src,$(INTERNAL_SOURCES),$(eval $(call GENERATE_SHARED_OBJECTS,$(src))))
+endif
+$(SHARED_OBJECTS): | $(OBJDIR)/shared $(DEPDIR)
+$(STATIC_OBJECTS): | $(OBJDIR)/static $(DEPDIR)
+
+$(OBJDIR):
+ $(call mkdir,"$@")
+$(DEPDIR):
+ $(call mkdir,"$@")
+$(OBJDIR)/static: $(OBJDIR)
+ $(call mkdir,"$@")
+$(OBJDIR)/shared: $(OBJDIR)
+ $(call mkdir,"$@")
+
+
+.PHONY: clean
+clean:
+ $(foreach target,$(CLEAN_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+ $(call rmdir,"$(OBJDIR)")
+ $(call rmdir,"$(DEPDIR)")
+ $(call rm,"lib$(MAIN_LIBRARY).so")
+ $(call rm,"lib$(MAIN_LIBRARY).a")
+ $(call rm,"$(DLLOUT)")
+ifeq ($(SAVEFLAGS),1)
+ $(call rm,"$(CFLAGS_TMPFILE)")
+ $(call rm,"$(LDFLAGS_TMPFILE)")
+endif
+
+#header file dep tracking
+-include $(wildcard $(DEPDIR)/*.d)
+
diff --git a/makefile.recursive b/makefile.recursive
new file mode 100644
index 0000000..b0e47b7
--- /dev/null
+++ b/makefile.recursive
@@ -0,0 +1,206 @@
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see .
+
+#Copyright 2018-2020 rexy712
+
+#Makefile to generate a single executable from all sources in SOURCE_DIRS that end in EXT
+
+ifeq ($(OS),Windows_NT)
+ WINDOWS::=1
+endif
+
+SOURCE_DIRS::=src
+SOURCES::=
+OBJDIR::=obj
+DEPDIR::=$(OBJDIR)/dep
+LIBDIRS::=lib
+INCLUDE_DIRS::=include
+CFLAGS::=-std=c18 -Wall -pedantic -Wextra
+CXXFLAGS::=-std=c++17 -Wall -pedantic -Wextra
+EXT::=cpp
+LANG::=$(EXT)
+MAIN_EXECUTABLE::=tester
+PRE_TARGETS::=
+POST_TARGETS::=
+CLEAN_TARGETS::=
+RELEASE?=0
+MEMCHK?=0
+SAVEFLAGS?=1
+
+ifneq ($(WINDOWS),1)
+ #*nix settings
+ CC::=gcc
+ CXX::=g++
+ LDLIBS::=
+ LDFLAGS::=
+ STRIP::=strip
+ RANLIB::=ranlib
+ AR::=ar
+ AS::=as
+else #windows
+ #windows settings
+ #windows is a fuckwit
+ MINGW_PREFIX::=x86_64-w64-mingw32-
+ CC::=$(MINGW_PREFIX)gcc
+ CXX::=$(MINGW_PREFIX)g++
+ LDLIBS::=
+ LDFLAGS::=
+ STRIP::=$(MINGW_PREFIX)strip
+ RANLIB::=$(MINGW_PREFIX)ranlib
+ AR::=$(MINGW_PREFIX)ar
+ AS::=$(MINGW_PREFIX)as
+endif #windows
+
+#Put your custom targets for PRE_TARGETS, POST_TARGETS, and CLEAN_TARGETS here:
+
+
+
+###########################################################################################################
+#Everything past this point is internal BS, probably best not to touch it unless you know what you're doing
+
+#set the all target as the default target, otherwise the topmost target will run
+.DEFAULT_GOAL::=all
+
+#set the main target to match the output of mingw
+ifeq ($(WINDOWS),1)
+ MAIN_EXECUTABLE::=$(MAIN_EXECUTABLE).exe
+endif
+
+#system dependant bullshit
+ifeq ($(OS),Windows_NT)
+ #windows' cmd commands
+ mkdir=mkdir $(subst /,\,$(1)) > NUL 2>&1
+ rm=del /F $(1) > NUL 2>&1
+ rmdir=rd /S /Q $(1) > NUL 2>&1
+ move=move /Y $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
+ copy=copy /Y /B $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
+else
+ #*nix terminal commands
+ mkdir=mkdir -p $(1)
+ rm=rm -f $(1)
+ rmdir=rm -rf $(1)
+ move=mv $(1) $(2)
+ copy=cp $(1) $(2)
+endif
+
+#setup compiler and flags based on language
+ifeq ($(LANG),cpp)
+ COMPILER_FLAGS::=$(CXXFLAGS)
+ COMPILER::=$(CXX)
+else ifeq ($(LANG),c)
+ COMPILER_FLAGS::=$(CFLAGS)
+ COMPILER::=$(CC)
+endif
+
+ifeq ($(RELEASE),1)
+ #a lot of false strict aliasing warnings from gcc 9
+ COMPILER_FLAGS+=-O2 -Wno-strict-aliasing
+else ifeq ($(MEMCHK),1)
+ #use asan to check memory leaks/invalid accesses
+ LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
+ COMPILER_FLAGS+=-O0 -g3 -ggdb -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
+else
+ #default target
+ COMPILER_FLAGS+=-O0 -g3 -ggdb
+endif
+
+ifeq ($(SAVEFLAGS),1)
+ CFLAGS_TMPFILE::=.cflags.tmp
+ OLD_COMPILEFLAGS::=$(file <$(CFLAGS_TMPFILE))
+ LDFLAGS_TMPFILE::=.ldflags.tmp
+ OLD_LINKFLAGS::=$(file <$(LDFLAGS_TMPFILE))
+endif
+
+#add dependency tracking and include directories
+INTERNAL_COMPILERFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))"
+INTERNAL_LINKFLAGS=$(foreach dir,$(LIBDIRS),-L"$(dir)")
+THIS_MAKEFILE_NAME::=$(lastword $(MAKEFILE_LIST))
+INTERNAL_SOURCES::=$(SOURCES) $(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext))))
+OBJECTS::=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(INTERNAL_SOURCES)))))
+ALL_COMPILEFLAGS=$(COMPILER_FLAGS) $(INTERNAL_COMPILERFLAGS)
+ALL_LINKFLAGS=$(INTERNAL_LINKFLAGS) $(LDFLAGS)
+
+#Arguments to make submake use this makefile without "Entering directory" stuff
+SUBMAKE_ARGUMENTS::=--no-print-directory -e -f "$(THIS_MAKEFILE_NAME)"
+#just a variable for a newline
+define \n
+
+
+endef
+
+.PHONY: cflags-update
+cflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(subst -MF"$(DEPDIR)/",-MF"$(DEPDIR)/cflags-update",$(ALL_COMPILEFLAGS)),$(OLD_COMPILEFLAGS))
+ $(file >$(CFLAGS_TMPFILE),$(ALL_COMPILEFLAGS))
+endif
+endif
+.PHONY: ldflags-update
+ldflags-update:
+ifeq ($(SAVEFLAGS),1)
+ifneq ($(ALL_LINKFLAGS),$(OLD_LINKFLAGS))
+ $(file >$(LDFLAGS_TMPFILE),$(ALL_LINKFLAGS))
+endif
+endif
+.PHONY: flags-update
+flags-update: cflags-update ldflags-update
+
+#default target: run targets in PRE_TARGETS, then the main executable, then POST_TARGETS
+.PHONY: all
+all: flags-update
+ $(foreach target,$(PRE_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+ @$(MAKE) $(SUBMAKE_ARGUMENTS) "$(MAIN_EXECUTABLE)"
+ $(foreach target,$(POST_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+
+#Link executable
+$(MAIN_EXECUTABLE): $(OBJECTS) $(LDFLAGS_TMPFILE)
+ $(COMPILER) $(OBJECTS) -o "$(basename $@)" $(ALL_LINKFLAGS) $(LDLIBS)
+ifeq ($(RELEASE),1)
+ $(STRIP) --strip-all "$@"
+endif
+
+#Object target recipe
+define GENERATE_OBJECTS
+$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).%.o: $(1)/% $(CFLAGS_TMPFILE)
+ $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
+endef
+define GENERATE_INDIVIDUAL_OBJECTS
+$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).o: $(1) $(CFLAGS_TMPFILE)
+ $$(COMPILER) $$(ALL_COMPILEFLAGS) "$$<" -o "$$@"
+endef
+
+#Create targets for object files
+$(foreach dir,$(SOURCE_DIRS),$(eval $(call GENERATE_OBJECTS,$(dir))))
+$(foreach src,$(SOURCES),$(eval $(call GENERATE_INDIVIDUAL_OBJECTS,$(src))))
+$(OBJECTS): | $(OBJDIR) $(DEPDIR)
+
+#Output directory creation
+$(OBJDIR):
+ $(call mkdir,"$@")
+$(DEPDIR):
+ $(call mkdir,"$@")
+
+.PHONY: clean
+clean:
+ $(foreach target,$(CLEAN_TARGETS),@$(MAKE) $(SUBMAKE_ARGUMENTS) "$(target)"$(\n))
+ $(call rmdir,"$(DEPDIR)")
+ $(call rmdir,"$(OBJDIR)")
+ $(call rm,"$(MAIN_EXECUTABLE)")
+ $(call rm,"$(MAIN_EXECUTABLE).exe")
+ifeq ($(SAVEFLAGS),1)
+ $(call rm,"$(CFLAGS_TMPFILE)")
+ $(call rm,"$(LDFLAGS_TMPFILE)")
+endif
+
+#header file dep tracking
+-include $(wildcard $(DEPDIR)/*.d)