+################################################################################
+# Desc: Mihrtec Standard Transpilation Makefile
+# Author: Mihrtec LLC
+# Date: 2016
+################################################################################
+# This makefile manages a build environment targeting native platforms with gcc
+# and web platforms with either emscripten or binaryen (js or wasm).
+################################################################################
+# Each .c file is autopresidential debate california timematically compiled into an environment-dependent object
+# file of compiler-specific format (.o for gcc, .bc for llvm/emcc/em++).
+################################################################################
+default: all
+# Source languages handled by this build system
+LANGS := c cpp go
+# Source-to-source languages handled by this build system
+SLANGS := y rl
+# Language-specific compilers and flags passed in from environment
+c_C := $(strip $(notdir $(CC)))
+c_FLAGS := $(strip $(CFLAGS)) -I.
+c_LIBS := SDL2 wolfssl
+c_SRCL := y
+cpp_C := $(strip $(notdir $(CXX)))
+cpp_FLAGS := $(strip $(CXXFLAGS)) $(c_FLAGS)
+cpp_LIBS := $(c_LIBS)
+go_C := gccgo
+go_FLAGS := $(c_FLAGS)
+# Source to source languages
+# Bison
+y_C := bison
+y_FLAGS := -d
+y_STEM := tab
+y_TRG := c h
+# Ragel
+rl_C := ragel
+rl_FLAGS := -C
+rl_TRG := c
+# Compiler-specific associations. Each compiler has a binary object suffix
+# (OBJ), an archiver (AR), and an archiver object suffix (AROBJ). Each compiler
+# may optionally have defined a linker (LD), and a binary output suffix (OUT).
+cc_OBJ := o
+cc_LD := $(LD)
+cc_AR := $(AR)
+cc_AROBJ := a
+cc_DBG := gdb
+$(cpp_C)_LD := $(cc_LD)
+gcc_OBJ := $(cc_OBJ)
+gcc_LD := $(cc_LD)
+ngcc_AR := $(cc_AR)
+gcc_AROBJ := $(cc_AROBJ)
+gcc_DBG := $(cc_DBG)
+emcc_OBJ := bc
+emcc_AR := emar
+emcc_AROBJ := $(emcc_OBJ) #emar is just a python script that reparses shit for emcc
+emcc_OUT := .js
+g++_OBJ := $(cc_OBJ)
+em++_OBJ := $(emcc_OBJ)
+em++_AR := $(emcc_AR)
+em++_AROBJ := $(emcc_AROBJ)
+em++_OUT := $(emcc_OUT)
+gccgo_OBJ := o
+gccgo_LD := $(cc_LD)
+gccgo_AR := $(cc_AR)
+gccgo_AROBJ := $(cc_AROBJ)
+# Shell functions to determine what libraries can be linked by the compiler
+cc_LDLIBS := $(shell ls /usr/lib | grep ".o" | sed -e 's@^.*lib\([_\+a-zA-Z0-9\-]*\)\..*@\1@g')
+gcc_LDLIBS := $(cc_LDLIBS)
+emcc_LDLIBS :=
+g++_LDLIBS := $(cc_LDLIBS)
+em++_LDLIBS :=
+go_LDLIBS := $(cc_LDLIBS)
+# Directories
+# The directory in './' containing app drivers and compilation info
+DRIVER_DIR := bin
+ROOT_DIR := ..
+LIB_DIR := $(ROOT_DIR)/lib$(shell uname -m)/$($(c_C)_OBJ)
+LIBDL_DIR := $(LIB_DIR)/.cache
+LIBINC_DIR := $(ROOT_DIR)/include
+# The makefile MUST be capable of generating these directories and all of their
+# contents. These directories are removed during the 'scrub' rule
+MAKE_DIRS := $(LIB_DIR) $(LIBDL_DIR) $(LCLLIB_DIR)
+# Set up lib inclusions, and scan for built libs
+c_FLAGS += -I$(LIBINC_DIR)
+c_OBJ := $($(c_C)_OBJ)
+# Modules are any directories other than 'DRIVER_MODULE' in './' and produce a
+# single object file in './' containing all of the module's symbols and
+# binaries.
+MODULES := $(filter-out $(DRIVER_DIR),$(subst /,,$(shell ls -d */)))
+
+#AWK_REVERSE_SQUASH##############################################################
+# The following awk program reverses the order of a list while also removing
+# duplicate entries. The effect of this when run on the dependency tree is that
+# it will remove duplicates occurring in reverse order, allowing the most deeply
+# nested libraries to be built, and linked, first. ##############################
+define AWK_REVERSE_SQUASH =
+awk \
+'
+{ for(i=NF;i>0;i--)
+ printf (!a[$$i]++) ? $$i FS : "";
+ i=split("",a);
+ print ""
+}
+'
+#/AWK_REVERSE_SQUASH#############################################################
+endef
+
+#PREMAKE_SOURCE_COMPILER#########################################################
+# Compile any source-to-source languages before executing the majority of make ##
+#################################################################################
+define PREMAKE_SOURCE_COMPILER =
+# Find the sources for each source-to-source language
+$(foreach slang,$(SLANGS),
+$(eval SLANG_SRC := $(patsubst ./%, %,$(shell find -name "*.$(slang)" -not -name ".*")))
+# Foreach target type in the source-to-source language, add this source's
+# targets to the list of the current source-to-source language targets
+$(eval undefine SLANG_TRG)
+$(foreach trg,$($(slang)_TRG),
+$(eval SLANG_TRG += $(SLANG_SRC:%.$(slang)=%.$(if $($(slang)_STEM),$($(slang)_STEM).)$(trg))))
+# Stat the source file's last-modified time to the var SRC_TIME
+$(foreach src,$(SLANG_SRC),
+# Establish a command to run for compiling this file
+$(eval SHELL_CMD := cd $(dir $(src)) && $($(slang)_C) $($(slang)_FLAGS) $(notdir $(src)))
+# Evaluate missing targets
+$(eval FOUND_TRG := $(shell find $(dir $(src)) -name "$(basename $(notdir $(src))).*" -not -name ".*"))
+$(eval MISSING_TRG := $(filter-out $(FOUND_TRG), $(SLANG_TRG)))
+# Check timings of existing files
+$(eval SRC_TIME := $(shell stat -c %Y $(src)))
+# For each of the targets created by this source language, evaluate the
+# last-modified times of each potential target. If the file does not exist, set
+# the time to '0', otherwise set it to the -c %Y result from statting the file.
+$(foreach srctrg,$(filter $(basename $(src))%, $(SLANG_TRG)),
+$(eval TRG_TIMES += $(if $(wildcard $(srctrg)),$(shell stat -c %Y $(srctrg)),0)==$(srctrg)))
+# Sort TRG_TIMES, which will produce a list of oldest-first files and timestamps
+# of the form [last-modified-deltatime]==[filename]
+$(eval TRG_TIMES := $(shell echo $(sort $(TRG_TIMES)) | $(call AWK_REVERSE_SQUASH)))
+# Evaluate the newest time from our ordered list by splitting the word up by its
+# '==' connectors and looking at the first word
+$(eval NEWEST_TRG_TIME := $(word 1,$(subst ==, ,$(TRG_TIMES))))
+# Find the older of the two times (between SRC_TIME and NEWEST_TRG_TIME)
+$(eval OLDER_TIME := $(firstword $(sort $(NEWEST_TRG_TIME) $(SRC_TIME))))
+# If the older of the two times is the newest target time found, then we need to
+# rebuild, but only if our build rule intends to actually make something. If it
+# does not intend to make something, drop a functional rule to actually make the
+# target
+$(if $(MAKECMDGOALS),
+$(eval BUILDGOALS := $(filter-out clean scrub purge uninstall,$(MAKECMDGOALS))),
+$(eval BUILDGOALS := all))
+$(if $(and $(BUILDGOALS),$(or $(MISSING_TRG), $(filter $(OLDER_TIME),$(NEWEST_TRG_TIME)))),
+$(if $(findstring n,$(MAKEFLAGS)),
+$(SLANG_TRG):
+ $(SHELL_CMD)
+,
+$(info $(SHELL_CMD) $(eval $(shell $(SHELL_CMD)))))
+))
+# Put these targets on the MAKE_TARGETS list to be removed during "clean",
+# regardless of whether or not they were built just now.
+$(eval MAKE_TARGETS += $(SLANG_TRG))
+)
+#/PREMAKE_SOURCE_COMPILER########################################################
+endef
+# Compile a source language to a language that compiles to binary, but only if
+# our build rule intends to build some kind of target
+$(eval $(call PREMAKE_SOURCE_COMPILER,))
+
+
+# Backup the CVS password and CVSROOT environment vars in case we change them
+$(if $(wildcard ~/.cvspass),\
+$(eval CVSPASS_BAK := $(file <~/.cvspass)))
+$(if $(CVSROOT),\
+$(eval CVSROOT_BAK := $(CVSROOT)))
+
+# A long function definition to grab deps from the source's compiler and route
+# errors to /dev/null if the rule generator expresses bogus errors
+define SRC_LANG_FLAGS_OPT_DEPS =
+$(filter-out \ %:,$(shell $($2_C) $3 $4 -MG $1 2> /dev/null))
+endef
+
+#SRC_LANG_RULE###################################################################
+# Given a source and a language, generate a rule to make the object. The second
+# invocation of gcc per file lists only its local includes, and filters out
+# anything already caught. Anything remaining should be considered to be
+# available in the current working directory of the source file(i.e. if you
+# '#include "something.h"' in 'mymodule/horseradish.c', you would expect to
+# include 'mymodule/something.h'. this check lets that happen), otherwise it is
+# some kind of user error #######################################################
+define SRC_LANG_RULE =
+$(if $($1),,$(eval $1 := t)\
+$(eval MOD := $(filter $(MODULES),$(firstword $(subst /, ,$(dir $1)))))\
+$(eval FLG := $(MOD:%=-I% ))\
+$(if $(wildcard $1),
+$(eval DEPS := $(filter-out \ %:,$(shell $($2_C) $(FLG) -M -MG $1)))\
+$(eval MDEPS := $(filter $(MODULES:%=%/%),$(DEPS)))\
+$(eval LOST := $(filter-out \ %: $(DRIVER_DIR)/% $(MODULES:%=%/%),$(shell $($2_C) $(FLG) -MM -MG $1)))\
+$(eval MDEPS += $(LOST:%=$(dir $1)%))\
+$(eval MDEPS := $(shell echo $(MDEPS) | sed -e 's@[a-zA-Z0-9\-\+/]*\.\./include@../include@g'))\
+$(eval ALLDEPS := $(MDEPS) $(DEPS)),\
+$(error Cannot generate deps for: $1, file not found))\
+$(eval SRC_OBJ := $(basename $1).$($2_OBJ))\
+$(eval MAKE_TARGETS += $(SRC_OBJ))
+# Assume that lost dependencies are in the folder with the source
+$(foreach lost,$(LOST),
+$(eval ALLDEPS := $(subst $(lost), $(dir $1)$(lost),$(ALLDEPS))))
+
+# Find any deps that aren't built yet, which the compiler has flagged
+# as missing, but which we know the expected location of libs for
+# this language should have their includes in 'LIBINC_DIR'
+$(eval BUILDDEPS := $(filter $($2_LIBS:%=%/%),$(ALLDEPS)))
+$(eval ALLDEPS := $(filter-out $(BUILDDEPS),$(ALLDEPS)) $(BUILDDEPS:%=$(LIBINC_DIR)/%))
+# Object for $1
+$(SRC_OBJ): $(ALLDEPS)
+ $($2_C) $$($2_FLAGS) $(FLG) -c -o $$@ $1
+$(if $($2_DBG),\
+$(eval DBG_OBJ := $(dir $1).$($2_DBG)/$(basename $(notdir $1)).$($2_OBJ))\
+$(if $(findstring $(dir $(DBG_OBJ)),$(MAKE_DIRS)),,\
+$(eval MAKE_DIRS += $(dir $(DBG_OBJ))))
+$(eval MAKE_TARGETS += $(DBG_OBJ))
+# Object for $1 with $($2_DBG) symbols
+$(DBG_OBJ): $(ALLDEPS) | $(dir $(DBG_OBJ))
+ $($2_C) $$($2_FLAGS) $(FLG) -Og -g$($2_DBG) -c -o $$@ $1
+))
+#/SRC_LANG_RULE##################################################################
+endef
+
+#SRC_LANG_DRVRULE################################################################
+# establish a build and link rule given a source driver and language ############
+define SRC_LANG_DRVRULE =
+$(eval DRIVER_NAME := $(basename $(notdir $1)))
+$(eval DRIVER_TARG := $(1:%.$2=$(ROOT_DIR)/%$($2_OUT)))
+$(eval DRIVER_SOB := $(basename $1).$($2_OBJ))
+
+# Generate a rule for the driver object
+$(eval $(call SRC_LANG_RULE,$1,$2))
+
+# Modules can be implicitly specified by a driver if its dependencies include
+# ANY files from any module directory. The 'MDEPS' var is set by SRC_LANG_RULE
+# and expresses the dependencies required to build the object file only.
+$(foreach module_dep,$(MDEPS),
+$(eval DRIVER_MODULES += $(firstword $(subst /, ,$(module_dep))))
+)
+$(eval DRIVER_MODULES := $(filter-out $(DRIVER_DIR) ..,$(sort $(DRIVER_MODULES))))
+
+# In the event that the driver developer does not wish to include a header to
+# any internal modules, the developer may instead create a file with the same
+# basename as the driver, but with the '.ld' suffix, which contains a space
+# separated list of internal modules to link together during compilation
+$(eval DRIVER_LDVALS := $(file <$(1:%.$2=%.ld)))
+$(eval DRIVER_LFLAGS := $(filter -l%,$(DRIVER_LDVALS)))
+$(eval DRIVER_MODULES += $(filter-out -l%,$(DRIVER_LDVALS)))
+
+# List of module archives to link together during compilation
+$(eval DRIVER_ARCHIVES := $(DRIVER_MODULES:%=%.$($2_AROBJ)))
+$(eval DRIVER_DEPS := $(DRIVER_SOB) $(DRIVER_ARCHIVES))
+$(eval DRIVER_DBGARCHIVES := $(DRIVER_MODULES:%=.$($2_DBG)/%.$($2_AROBJ)))
+$(eval DRIVER_DBGDEPS := $(dir $(DRIVER_SOB)).$($2_DBG)/$(notdir $(DRIVER_SOB)))
+$(eval DRIVER_DBGDEPS += $(DRIVER_DBGARCHIVES))
+
+# If the compiler supports linking, distinguish static from dynamic links,
+# otherwise set all libraries to static
+$(if $($($2_C)_LD),\
+$(eval STLIBS := $(filter-out $($($2_C)_LDLIBS),$($2_LIBS)))\
+$(eval DLIBS := $(filter-out $(STLIBS),$($2_LIBS))),\
+$(eval STLIBS := $($2_LIBS)))
+
+# Directory setup
+$(eval DRIVER_TARG_DIR := $(dir $(1:%.$2=$(ROOT_DIR)/%)))
+$(eval MAKE_DIRS += $(DRIVER_TARG_DIR))
+$(if $($2_DBG),$(eval MAKE_DIRS += $(DRIVER_TARG_DIR).$($2_DBG)))