################################################################################
#
# Makefile for the Swift implementation of MAL.
#
# The MAL project consists of building up a dialect/subset of Clojure over a
# series of steps. Each step implements a new feature or concept in an easily
# understandable and approachable manner. Each step can be built on its own and
# tested. Each step is built from a step-specific "step.swift" file and a set
# of files common to all steps.
#
# The general approach in this file is to discover the set of "step" source
# files (step0_repl.swift, etc.), and build corresponding executable files
# (step0_repl, etc) from them and from the set of supporting Swift files.
# Since the set of "step" files is discovered on-the-fly, the rules to make
# those files are also generated on-the-fly using $(eval).
#
# The various "step0_repl.swift", etc., source files are actually generated
# from a file called "templates/step.swift". Since each "step" file
# incrementally builds towards the final, complete "step" file,
# "templates/step.swift" is -- for the most part -- a copy of this final "step"
# file with each line annotated with the step in which that line is introduced.
# Through the use of a simple filter program, the "templates/step.swift" file
# can then be processed to produce each intermediate "step" file. This Makefile
# takes care of performing that processing any time "templates/step.swift"
# changes.
#
# MAKE TARGETS:
#
# 	all:
# 		Make all step targets, (re)generating source files if needed.
# 	alls:
# 		(Re)generate source files, if needed.
# 	step0_repl, step1_read_print, etc.:
# 		Make the corresponding step target.
# 	s0...sN:
# 		Shortcuts for the previous targets.
# 	step0_repl.swift, step1_read_print.swift, etc.:
# 		(Re)generate source files for the corresponding step target, if
# 		needed.
# 	ss0...ssN:
# 		Shortcuts for the previous targets.
# 	clean:
# 		Delete all built executables. Generated source files are *not*
# 		deleted.
# 	dump:
# 		Print some Make variables for debugging.
#
# TODO:
#	* Compile each .swift file into an intermediate .o file and link the .o
#	  files, rather than performing a complete build of all files any time
#	  any one of them is out-of-date. Here are the commands generated when
#	  using `swiftc -v`:
#
#	  /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift \
#	  	-frontend \
#	  	-c \
#	  	-primary-file stepA_mal.swift \
#	  	./core.swift \
#	  	./env.swift \
#	  	./main.swift \
#	  	./printer.swift \
#	  	./reader.swift \
#	  	./readline.swift \
#	  	./types.swift \
#	  	-target x86_64-apple-darwin14.1.0 \
#	  	-target-cpu core2 \
#	  	-sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk \
#	  	-import-objc-header ./bridging-header.h \
#	  	-color-diagnostics \
#	  	-Onone \
#	  	-ledit \
#	  	-module-name stepA_mal \
#	  	-o /var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/stepA_mal-e0a836.o
#	  ... Similar for each source file...
#	  /usr/bin/ld \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/stepA_mal-e0a836.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/core-28b620.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/env-5d8422.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/main-e79633.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/printer-cdd3e5.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/reader-bb188a.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/readline-53df55.o \
#	  	/var/folders/dj/p3tx6v852sl88g79qvhhc2ch0000gp/T/types-7cb250.o \
#	  	-L /usr/lib \
#	  	-ledit \
#	  	-syslibroot \
#	  	/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk \
#	  	-lSystem \
#	  	-arch x86_64 \
#	  	-L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
#	  	-rpath /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
#	  	-macosx_version_min 10.10.0 \
#	  	-no_objc_category_merging \
#	  	-o stepA_mal
#
#	* Consider adding a clean-dist (or similar) that deletes the generated
#	  "step" source files.
#
################################################################################

#
# Discover the set of "step" source files (those having the form
# "step<#>_foo.swift")
#
SRCS := $(wildcard ./step*.swift)

#
# From the set of "step" source files, generate the set of executable files
# (those having the form "step<#>_foo")
#
EXES := $(patsubst %.swift,%,$(SRCS))

#
# Also generate references to any debug-symbol directories we may make when -g
# is specified.
#
DSYMS := $(patsubst %.swift,%.dSYM,$(SRCS))

#
# Given a name like "./step<#>_foo", return <#>.
#
# (Is there a better way to do this? $(patsubst) seems to be the most
# appropriate built-in command, but it doesn't seem powerful enough.)
#
# (I've included a `sed` version in case relying on bash is contraindicated.)
#
get_step_number = $(shell echo $(1) | sed -e "s/.*step\(.\).*/\1/")
#get_step_number = $(shell [[ $(1) =~ step(.)_.* ]] ; echo $${BASH_REMATCH[1]})

#
# Working from the list of discovered "step<#>_foo.swift" files, generate the
# list of step numbers.
#
get_all_step_numbers = $(foreach SRC,$(SRCS),$(call get_step_number,$(SRC)))

#
# Generate the dependencies for the "all" target. This list has the form
# "s0 s1 ... sN" for all N returned by get_all_step_numbers. That is:
#
# 	all: s0 s1 ... sN
#
# Also create an "alls" target that just regenerates all the "step" files from
# the corresponding template file.
#
$(eval all: $(patsubst %,s%,$(call get_all_step_numbers)))
$(eval alls: $(patsubst %,ss%,$(call get_all_step_numbers)))

#
# Generate the dependencies for the ".PHONY" target. That is:
#
# 	.PHONY: all clean dump s0 s1 ... sN
#
$(eval .PHONY: all clean dump $(patsubst %,s%,$(call get_all_step_numbers)))

#
# Define the "EZ" targets, where "s0" builds "step0_repl", "s1" builds
# "step1_read_print", etc. That is:
#
# 	s0: step0_repl
# 	s1: step1_read_print
# 	...
# 	sN: stepN_foo
#
# Also create corresponding targets that rebuild the sources files:
#
# 	ss0: step0_repl.swift
# 	ss1: step1_read_print.swift
# 	...
# 	ssN: stepN_foo.swift
#
$(foreach EXE,$(EXES),$(eval s$(call get_step_number,$(EXE)): $(EXE)))
$(foreach SRC,$(SRCS),$(eval ss$(call get_step_number,$(SRC)): $(SRC)))

#
# Various helpful variables.
#
DEV_DIR		:= $(firstword $(wildcard /Applications/Xcode-beta.app /Applications/Xcode.app))
SWIFT		:= $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --find swiftc 2>/dev/null)
SDKROOT		:= $(shell DEVELOPER_DIR="$(DEV_DIR)" xcrun --show-sdk-path 2>/dev/null)
STEP_TEMPLATE	:= ./templates/step.swift
FILTER	 	:= ./templates/filter_steps.sh
UTIL_SRC	:= $(filter-out $(STEP_TEMPLATE) $(SRCS),$(wildcard ./*.swift))
ifndef TYPES
TYPES		:= CLASS
endif
ifeq ($(TYPES), ENUM)
UTIL_SRC	:= $(filter-out ./types_class.swift,$(UTIL_SRC))
else
UTIL_SRC	:= $(filter-out ./types_enum.swift,$(UTIL_SRC))
endif
OPT		:= -Ounchecked -whole-module-optimization
DEBUG		:= #-g
EXTRA		:= #-v
COMMON		:= $(UTIL_SRC) $(OPT) $(DEBUG) $(EXTRA) -import-objc-header ./bridging-header.h -L /usr/lib -ledit -sdk $(SDKROOT)

#
# Build the executable from the input sources consisting of the appropriate
# "step" file and the supporting files in $(UTIL_SRC).
#
$(EXES) : % : %.swift $(UTIL_SRC) ./Makefile
	@echo "Making    : $@"
	@$(SWIFT) $< $(COMMON) -o $@

#
# Build the "step" source file ("step<#>_foo.swift") from the step template
# file that combines all the steps in one file.
#
$(SRCS) : % : $(STEP_TEMPLATE) ./Makefile
	@echo "Generating: $@"
	@$(FILTER) $(call get_step_number,$@) $< $@

#
# Delete all of the build output (other than generated "step" source files)
#
clean:
	@rm -rf $(EXES) $(DSYMS)

#
# Display some variables for debugging.
#
dump:
	@echo "   SRCS = $(SRCS)"
	@echo "   EXES = $(EXES)"
	@echo "  DSYMS = $(DSYMS)"
	@echo "   UTIL = $(UTIL_SRC)"
	@echo "  SWIFT = $(SWIFT)"
	@echo "SDKROOT = $(SDKROOT)"
	@echo "  STEPS = $(call get_all_step_numbers)"
