srcdir = @srcdir@
VPATH = @srcdir@

include config.mk
include src/config.mk
include @srcdir@/files.mk

SMLLEX = src/ml-lex/smllex
SMLYACC = src/ml-yacc/smlyacc
SMLFORMAT = src/smlformat/smlformat
SMLSHARP = src/compiler/smlsharp
LLVM_PLUGIN = src/llvm/smlsharp_gc-$(LLVM_VERSION).$(DLLEXT)
SMLSHARP_ENV = SMLSHARP_HEAPSIZE=32M:2G
NETLIB_CFLAGS = $(CFLAGS) -DIEEE_8087 -DMALLOC=sml_xmalloc -DLong=int

RUNTIME_OBJECTS = \
  src/runtime/callback.$(OBJEXT) \
  src/runtime/control.$(OBJEXT) \
  src/runtime/error.$(OBJEXT) \
  src/runtime/exn.$(OBJEXT) \
  src/runtime/finalize.$(OBJEXT) \
  src/runtime/init.$(OBJEXT) \
  src/runtime/object.$(OBJEXT) \
  src/runtime/xmalloc.$(OBJEXT) \
  src/runtime/prim.$(OBJEXT) \
  src/runtime/splay.$(OBJEXT) \
  src/runtime/top.$(OBJEXT) \
  src/runtime/heap_concurrent.$(OBJEXT) \
  src/sql/main/builtintypes/timestamp/string_to_time_t.$(OBJEXT) \
  src/sql/main/builtintypes/timestamp/timeval_to_string.$(OBJEXT)
RUNTIME_DEBUG_OBJECTS = \
  src/runtime/livecheck.$(OBJEXT) \
  src/runtime/dbglog.$(OBJEXT)
LIBSMLSHARP_OBJECTS = \
  $(RUNTIME_OBJECTS) \
  src/runtime/call_with_cleanup.$(OBJEXT) \
  src/runtime/netlib/dtoa.$(OBJEXT)
LIBSMLSHARP_TARGETS = \
  src/runtime/main.$(OBJEXT) \
  src/runtime/libsmlsharp.$(LIBEXT)
COMPILER_SUPPORT_OBJECTS = \
  src/runtime/signal.$(OBJEXT)
TEST_SUPPORT_OBJECTS = \
  tests/driver/process.$(OBJEXT)
LLVM_PLUGIN_OBJECTS = \
  src/llvm/main/smlsharp_gc-$(LLVM_VERSION).$(OBJEXT)
ALL_GEN_FILES = \
  $(GEN_SOURCES) \
  $(GEN_MISC_FILES) \
  filemap \
  $(PRECOMPILED_OBJECTS) \
  minismlsharp
ALL_GEN_TARGETS = \
  $(LIBSMLSHARP_TARGETS) \
  $(LLVM_PLUGIN) \
  $(SMLLEX) \
  $(SMLYACC) \
  $(SMLFORMAT) \
  $(SMLSHARP)
ALL_TARGETS = \
  $(ALL_GEN_TARGETS) \
  $(INSTALL_FILES_SRC) \
  $(INSTALL_FILES_OBJ)
LINK_DEP = \
  filemap \
  $(LIBSMLSHARP_TARGETS) \
  $(LLVM_PLUGIN) \
  $(SMLSHARP_DEP)

all: $(ALL_TARGETS)

clean: PHONY
	-rm -f $(ALL_GEN_TARGETS)
	-rm -f $(LIBSMLSHARP_OBJECTS) $(LLVM_PLUGIN_OBJECTS)
	-rm -f $(COMPILER_SUPPORT_OBJECTS) $(TEST_SUPPORT_OBJECTS)
	-rm -f $(ALL_OBJECTS) $(ALL_GEN_FILES)
@MKREL@	-rm -f RELEASE

distclean: PHONY clean
	-rm -rf autom4te.cache config.h config.log config.status
	-rm -f Makefile config.mk src/config.mk stamp-h

sources: PHONY $(GEN_SOURCES)
runtime: PHONY $(LIBSMLSHARP_TARGETS)

install: PHONY $(ALL_TARGETS)
	@copy () { \
	   dir=$${2%/*}; \
	   dir=$${dir#src}; \
	   dir='$(DESTDIR)$(libdir_smlsharp)'"$$dir"; \
	   test -d "$$dir" || $(INSTALL) -d "$$dir" || exit; \
	   echo $(INSTALL_DATA) "$$1" "$$dir"; \
	   $(INSTALL_DATA) "$$1" "$$dir" || exit; \
	}; \
	for i in $(INSTALL_FILES_SRC); do copy '$(srcdir)/'"$$i" "$$i"; done; \
	for i in $(INSTALL_FILES_OBJ); do copy "$$i" "$$i"; done
	test ! -f src/compiler/smlsharp.lib \
	|| { $(INSTALL) -d '$(DESTDIR)$(libdir_smlsharp)/src/compiler'; \
	     $(INSTALL_DATA) src/compiler/smlsharp.lib \
	       '$(DESTDIR)/$(libdir_smsharp)/compiler'; }
	$(INSTALL) -d '$(DESTDIR)$(libdir_smlsharp)'
	$(INSTALL_DATA) src/config.mk \
	  '$(DESTDIR)$(libdir_smlsharp)/config.mk'
	$(INSTALL_DATA) $(srcdir)/src/builtin.smi \
	  '$(DESTDIR)$(libdir_smlsharp)/builtin.smi'
	$(INSTALL) -d '$(DESTDIR)$(libdir_smlsharp)/runtime'
	$(INSTALL_DATA) src/runtime/libsmlsharp.$(LIBEXT) \
	  '$(DESTDIR)$(libdir_smlsharp)/runtime'
	$(INSTALL_DATA) src/runtime/main.$(OBJEXT) \
	  '$(DESTDIR)$(libdir_smlsharp)/runtime'
	$(INSTALL_DATA) $(COMPILER_SUPPORT_OBJECTS) \
	  '$(DESTDIR)$(libdir_smlsharp)/runtime'
	$(INSTALL) -d '$(DESTDIR)$(libdir_smlsharp)/llvm'
	$(INSTALL_PROGRAM) $(LLVM_PLUGIN) '$(DESTDIR)$(libdir_smlsharp)/llvm'
	$(INSTALL) -d '$(DESTDIR)$(bindir)'
	$(INSTALL_PROGRAM) $(SMLLEX) '$(DESTDIR)$(bindir)'
	$(INSTALL_PROGRAM) $(SMLYACC) '$(DESTDIR)$(bindir)'
	$(INSTALL_PROGRAM) $(SMLFORMAT) '$(DESTDIR)$(bindir)'
	$(INSTALL_PROGRAM) $(SMLSHARP) '$(DESTDIR)$(bindir)'
	$(INSTALL) -d '$(DESTDIR)/$(mandir)/man1'
	$(INSTALL_DATA) man/smllex.1 '$(DESTDIR)/$(mandir)/man1'
	$(INSTALL_DATA) man/smlyacc.1 '$(DESTDIR)/$(mandir)/man1'
	$(INSTALL_DATA) man/smlformat.1 '$(DESTDIR)/$(mandir)/man1'
	$(INSTALL_DATA) man/smlsharp.1 '$(DESTDIR)/$(mandir)/man1'

test: PHONY tests/driver/dotest
	$(SMLSHARP_ENV) tests/driver/dotest

stage: PHONY src/compiler/minismlsharp
	@n=`ls minismlsharp* | sed 's/minismlsharp//' | sort -n | tail -n1` \
	&& n=$$(($${n:--1}+1)) \
	&& mv -f -v minismlsharp "minismlsharp$$n" \
	&& mv -f -v src/compiler/minismlsharp minismlsharp \
	&& echo "** Stage $$(($$n+1)) build completed.  Rerun 'make stage' for further stages, or run 'make all' for stage $$(($$n+2)) full build."

# -------- implicit rules --------

PHONY: # force to rebuild

.POSIX:
.SUFFIXES: .c .@OBJEXT@ .sml .smi .sig .ppg .lex .grm

.sml.@OBJEXT@:
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath -filemap=filemap $(SMLFLAGS) -c -o $@ $<
.c.@OBJEXT@:
	$(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) -c -o $@ $<

# -------- runtime --------

src/runtime/libsmlsharp.$(LIBEXT): $(LIBSMLSHARP_OBJECTS)
	-rm -f $@
	$(AR) qc $@ $(LIBSMLSHARP_OBJECTS)
	-$(RANLIB) $@

src/runtime/netlib/dtoa.$(OBJEXT): $(srcdir)/src/runtime/netlib/dtoa.c
	$(CC) $(NETLIB_CFLAGS) -c -o $@ $(srcdir)/src/runtime/netlib/dtoa.c

src/runtime/exn.$(OBJEXT): $(srcdir)/src/runtime/exn.c
	$(CC) $(CFLAGS) -fexceptions $(DEFS) $(CPPFLAGS) -c -o $@ $(srcdir)/src/runtime/exn.c

src/runtime/control.$(OBJEXT): $(srcdir)/src/runtime/control.c
	$(CC) $(CFLAGS) -fexceptions $(DEFS) $(CPPFLAGS) -c -o $@ $(srcdir)/src/runtime/control.c

src/runtime/dbglog.$(OBJEXT): $(srcdir)/src/runtime/dbglog.c
	$(CC) -Wformat=0 $(CFLAGS) $(DEFS) $(CPPFLAGS) -c -o $@ $(srcdir)/src/runtime/dbglog.c

src/runtime/call_with_cleanup.o: $(srcdir)/src/runtime/call_with_cleanup.ll
	$(LLC) -mtriple=$(target) -O2 $(LLCFLAGS) -filetype=obj -o $@ $(srcdir)/src/runtime/call_with_cleanup.ll

# -------- compiler support --------

src/llvm/main/smlsharp_gc-$(LLVM_VERSION).$(OBJEXT): \
  $(srcdir)/src/llvm/main/smlsharp_gc.cpp
	$(CXX) $(LLVM_CXXFLAGS) -fPIC -c -o $@ $(srcdir)/src/llvm/main/smlsharp_gc.cpp

$(LLVM_PLUGIN): \
  src/llvm/main/smlsharp_gc-$(LLVM_VERSION).$(OBJEXT)
	$(CXX) $(RUNLOOP_DLDFLAGS) -o $@ src/llvm/main/smlsharp_gc-$(LLVM_VERSION).$(OBJEXT)

tests/driver/process.$(OBJEXT): $(srcdir)/tests/driver/process.c
	$(CC) $(CFLAGS) $(DEFS) $(CPPFLAGS) -c -o $@ $(srcdir)/tests/driver/process.c

# -------- link file map --------

filemap: Makefile $(srcdir)/files.mk
	set -ex; \
	srcdir='$(srcdir)/'; \
	srcdir=$${srcdir#./}; \
	src='$(srcdir)/files.mk'; \
	all=`mktemp`; \
	gen=`mktemp`; \
	obj=`mktemp`; \
	trap 'rm -f "$$all" "$$gen" "$$obj"' EXIT; \
	trap 'exit 127' INT QUIT STOP; \
	sed -e '1,/^ALL_OBJECTS =/d;/ =/,$$d;s/[ \\]//g' \
	    "$$src" > "$$all"; \
	sed -e '1,/^GEN_SOURCES =/d;/ =/,$$d;s/[ \\]//g;s/sml$$/$(OBJEXT)/' \
	    "$$src" > "$$gen"; \
	cat "$$all" "$$gen" | sort | uniq -u > "$$obj"; \
	sed -e "s'.*'= $${srcdir}& &'" \
	    "$$obj" > $@; \
	sed -e "s'.*'= $${srcdir}& &'" \
	    "$$gen" >> $@; \
	sed -e "s/$(OBJEXT)\$$/smi/;s'.*'= $${srcdir}& $${srcdir}&'" \
	    "$$obj" >> $@; \
	sed -e "s/$(OBJEXT)\$$/smi/;s'.*'= & $${srcdir}&'" \
	    "$$gen" >> $@

# -------- minismlsharp --------

PRECOMPILE_LLC = \
  $(LLC) -load=$(LLVM_PLUGIN) -mtriple=$(target) -O2 $(LLCFLAGS) \
  -tailcallopt -filetype=obj

include @srcdir@/precompiled/Makefile

./minismlsharp: $(LIBSMLSHARP_TARGETS) $(COMPILER_SUPPORT_OBJECTS) \
                $(PRECOMPILED_OBJECTS)
	$(CC) $(LDFLAGS) src/runtime/main.$(OBJEXT) \
	$(PRECOMPILED_OBJECTS) $(COMPILER_SUPPORT_OBJECTS) \
	src/runtime/libsmlsharp.$(LIBEXT) $(LIBS) -o $@

# -------- executables --------

$(SMLLEX): $(LINK_DEP) $(SMLLEX_OBJECTS)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFLAGS) \
	-filemap=filemap \
	$(srcdir)/src/ml-lex/ml-lex.smi -o $@

$(SMLYACC): $(LINK_DEP) $(SMLYACC_OBJECTS)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFLAGS) \
	-filemap=filemap \
	$(srcdir)/src/ml-yacc/src/ml-yacc.smi -o $@

$(SMLFORMAT): $(LINK_DEP) $(SMLFORMAT_OBJECTS)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFLAGS) \
	-filemap=filemap \
	$(srcdir)/src/smlformat/generator/main/call-main.smi -o $@

$(SMLSHARP): $(LINK_DEP) $(COMPILER_SUPPORT_OBJECTS) $(COMPILER_LIB_OBJECTS)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFLAGS) \
	$(RDYNAMIC_SMLFLAGS) -filemap=filemap \
	$(srcdir)/src/compiler/smlsharp.smi $(COMPILER_SUPPORT_OBJECTS) -o $@

src/compiler/minismlsharp: \
  $(LINK_DEP) $(COMPILER_SUPPORT_OBJECTS) $(MINISMLSHARP_OBJECTS)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFLAGS) \
	-filemap=filemap $(srcdir)/src/compiler/minismlsharp.smi \
	$(COMPILER_SUPPORT_OBJECTS) -o $@

tests/driver/dotest: $(LINK_DEP) $(TEST_OBJECTS) tests/driver/process.$(OBJEXT)
	$(SMLSHARP_ENV) $(MINISMLSHARP) -B src -nostdpath $(SMLFILE) \
	$(RDYNAMIC_SMLFLAGS) -filemap=filemap \
	$(srcdir)/tests/driver/dotest.smi tests/driver/process.$(OBJEXT) \
	$(COMPILER_SUPPORT_OBJECTS) -o $@

# -------- source code generation --------

@MKREL@RELEASE: mkdist \
@MKREL@  $(shell x=`sh $(srcdir)/mkdist release`; \
@MKREL@          y=`cat RELEASE 2>/dev/null`; \
@MKREL@          test "_$$x" != "_$$y" && echo PHONY)
@MKREL@	sh $(srcdir)/mkdist release > $@

src/config/main/Version.sml: \
  $(srcdir)/src/config/main/Version.sml.in RELEASE
	sed -e 's"%RELEASE%"'"`cat @RELEASE@`"'"' \
	    -e 's"%LIBDIR_SMLSHARP%"$(libdir_smlsharp)"' \
	$(srcdir)/src/config/main/Version.sml.in > $@

src/config/main/SQLConfig.sml: \
  $(srcdir)/src/config/main/SQLConfig.sml.in src/config.mk
	sed -e 's"%DLLEXT%"$(DLLEXT)"' \
	$(srcdir)/src/config/main/SQLConfig.sml.in > $@

src/compiler/compilerIRs/absyn/main/AbsynFormatter.sml: \
  src/compiler/compilerIRs/absyn/main/Absyn.sml \
  $(SMLFORMAT_DEP)
	$(SMLSHARP_ENV) $(MINISMLFORMAT) --output=$@ --separate=AbsynFormatter $(srcdir)/src/compiler/compilerIRs/absyn/main/Absyn.sml

src/compiler/compilerIRs/absyn/main/AbsynTyFormatter.sml: \
  src/compiler/compilerIRs/absyn/main/AbsynTy.sml \
  $(SMLFORMAT_DEP)
	$(SMLSHARP_ENV) $(MINISMLFORMAT) --output=$@ --separate=AbsynTyFormatter $(srcdir)/src/compiler/compilerIRs/absyn/main/AbsynTy.sml

src/compiler/compilerIRs/absyn/main/AbsynConstFormatter.sml: \
  src/compiler/compilerIRs/absyn/main/AbsynConst.sml \
  $(SMLFORMAT_DEP)
	$(SMLSHARP_ENV) $(MINISMLFORMAT) --output=$@ --separate=AbsynConstFormatter $(srcdir)/src/compiler/compilerIRs/absyn/main/AbsynConst.sml

src/compiler/compilerIRs/absyn/main/AbsynSQLFormatter.sml: \
  src/compiler/compilerIRs/absyn/main/AbsynSQL.sml \
  $(SMLFORMAT_DEP)
	$(SMLSHARP_ENV) $(MINISMLFORMAT) --output=$@ --separate=AbsynSQLFormatter $(srcdir)/src/compiler/compilerIRs/absyn/main/AbsynSQL.sml

# ---- autoconf ----

CHECK_GNUMAKE = \
    if $(MAKE) --version 2> /dev/null | grep 'GNU Make' > /dev/null; \
    then :; else echo 'Makefiles are updated, rerun make.'; exit 1; fi

$(srcdir)/configure: configure.ac
	cd '$(srcdir)' && autoconf

config.status: configure
	./config.status --recheck

Makefile: Makefile.in config.status
	./config.status Makefile
	@$(CHECK_GNUMAKE)

config.mk: config.mk.in config.status
	./config.status $@
	@$(CHECK_GNUMAKE)

src/config.mk: src/config.mk.in config.status
	./config.status $@
	@$(CHECK_GNUMAKE)

$(srcdir)/stamp-h.in: configure.ac
	cd '$(srcdir)' && autoheader
	echo timestamp > '$(srcdir)/stamp-h.in'
$(srcdir)/config.h.in: stamp-h.in

stamp-h: config.h.in config.status
	./config.status config.h stamp-h
config.h: stamp-h

src/config/main/C_dlfcn.sml: \
  $(srcdir)/src/config/main/C_dlfcn.sml.in config.status
	./config.status $@
src/config/main/C_errno.sml.sml: \
  $(srcdir)/src/config/main/C_errno.sml.in config.status
	./config.status $@
src/config/main/C_gmp.sml: \
  $(srcdir)/src/config/main/C_gmp.sml.sml.in config.status
	./config.status $@
src/config/main/C_myth.smi.sml: \
  $(srcdir)/src/config/main/C_myth.smi.sml.in config.status
	./config.status $@
src/config/main/C_myth.sml.sml: \
  $(srcdir)/src/config/main/C_myth.sml.sml.in config.status
	./config.status $@
src/config/main/C_pthread.smi: \
  $(srcdir)/src/config/main/C_pthread.smi.in config.status
	./config.status $@
src/config/main/C_pthread.sml: \
  $(srcdir)/src/config/main/C_pthread.sml.in config.status
	./config.status $@
src/config/main/C_signal.sml: \
  $(srcdir)/src/config/main/C_signal.sml.in config.status
	./config.status $@
src/config/main/C_stddef.smi: \
  $(srcdir)/src/config/main/C_stddef.smi.in config.status
	./config.status $@
src/config/main/C_stddef.sml.sml: \
  $(srcdir)/src/config/main/C_stddef.sml.in config.status
	./config.status $@
src/config/main/C_type.smi: \
  $(srcdir)/src/config/main/C_type.smi.in config.status
	./config.status $@
src/config/main/C_type.sml: \
  $(srcdir)/src/config/main/C_type.sml.in config.status
	./config.status $@

# -------- dependencies --------

depend: PHONY minismlsharp
	test '.' = '$(srcdir)'
	rm -f depend_ml.mk files_ml.mk depend_c.mk
	@$(SMLSHARP_ENV) SMLSHARP='$(MINISMLSHARP)' OBJEXT='$(OBJEXT)' \
	SMLFLAGS='$(SMLFLAGS)' \
	files_mk=files_ml.mk depend_mk=depend_ml.mk \
	$(SHELL) mkdepend \
	  SMLLEX         src/ml-lex/ml-lex.smi \
	  SMLYACC_LIB    src/ml-yacc-lib.smi \
	  SMLYACC        src/ml-yacc/src/ml-yacc.smi \
	  SMLFORMAT_LIB  src/smlformat-lib.smi \
	  SMLUNIT_LIB    src/smlunit-lib.smi \
	  SMLFORMAT      src/smlformat/generator/main/call-main.smi \
	  MINISMLSHARP   src/compiler/minismlsharp.smi \
	  COMPILER_LIB   src/compiler/smlsharp.smi \
	  PRELUDE_LIB    src/prelude.smi \
	  THREAD_LIB     src/thread.smi \
          SMLNJ_LIB      src/smlnj-lib/Util/smlnj-lib.smi \
          SMLNJ_JSON_LIB src/smlnj-lib/JSON/json-lib.smi \
	  TEST           tests/driver/dotest.smi
	@set -e; \
	for i in $(RUNTIME_OBJECTS:.$(OBJEXT)=.c) \
	         $(RUNTIME_DEBUG_OBJECTS:.$(OBJEXT)=.c) \
	         $(COMPILER_SUPPORT_OBJECTS:.$(OBJEXT)=.c) \
	         $(TEST_SUPPORT_OBJECTS:.$(OBJEXT)=.c) \
	         src/runtime/main.c; do \
	  echo $(CC) -E -MM $(DEFS) $(CPPFLAGS) $$i 1>&2; \
	  tmp=`$(CC) -E -MM $(DEFS) $(CPPFLAGS) $$i`; \
	  echo "$$tmp" \
	  | perl -0npe 's/[\s\\]+/ /g; s/\s*\z/ /;' \
	            -e 's/\A(.+?).o: (.+?)\1.c/\2\1.o: \2\1.c/;' \
	            -e 's/ \/[^ ]*(?= )//g;' \
	            -e 's/ +/ \\\n /g; s/[\s\\]+\z/\n/;' \
	            -e 's/^ (?!config.h)/ \$$(srcdir)\//mg' \
	    >> depend_c.mk; \
	done
	cat files_ml.mk > files.mk
	cat depend_ml.mk depend_c.mk > depend.mk
	rm -f files_ml.mk depend_ml.mk depend_c.mk

include @srcdir@/depend.mk
