#-----------------------------------------------------------------------------#
# Copyright (C) 1997-2011 The University of Melbourne.
# This file may only be copied under the terms of the GNU General
# Public License - see the file COPYING in the Mercury distribution.
#-----------------------------------------------------------------------------#

# library/Mmake - this is the main part of the Makefile
# for building the Mercury libraries.

#-----------------------------------------------------------------------------#
# These need to be defined before we include Mmake.common,
# so that they can be overridden in Mmake.params.
EXTRA_LDFLAGS =
EXTRA_LDLIBS =
#-----------------------------------------------------------------------------#

# Specify which files to check for namespace cleanliness, and which name
# prefixes are allowed.

CHECK_HDRS  =
CHECK_MHDRS = $(mer_std.mhs)
CHECK_OBJS  = $(mer_std.os)
ALLOW_LIB_PREFIX=yes
ALLOW_BROWSER_PREFIX=no
ALLOW_MDBCOMP_PREFIX=no
ALLOW_SSDB_PREFIX=no

MERCURY_DIR=..
LINK_RUNTIME_ONLY=yes
include $(MERCURY_DIR)/Mmake.common
-include Mmake.library.params

# Override the default rule in `mmake --use-mmc-make' that asks `mmc' to
# create a missing optional params file.
Mmake.library.params:

# Module-specific options should go in Mercury.options so they
# can be found by `mmc --make'.
include Mercury.options

MERCURY_MAIN_MODULES = mer_std

MAIN_TARGET=all

VPATH=.

#-----------------------------------------------------------------------------#

# Specify which options to use to compile the library.
# Don't change these without good reason - if you want to do a temporary
# change, change ../Mmake.params, or create Mmake.library.params.

ifeq ($(LIBRARY_INTERMODULE),yes)

# XXX Smart recompilation doesn't work with `--intermodule-optimization'.
# We still want to generate version numbers in the interface files, so
# just disable the warnings in INTER_FLAGS.
# If you want to actually check termination for the library, then you need
# to add --check-termination to INTER_FLAGS, but that is not enabled by default
# because it probably just results in spurious warnings.
# A different flags file is used when `--use-mmc-make' is in effect as
# some options are (currently) incompatible with `mmc --make'.
ifeq ($(MMAKE_USE_MMC_MAKE),yes)
INTER_FLAGS = --flags INTER_FLAGS_MMC_MAKE
else
INTER_FLAGS = --flags INTER_FLAGS
endif

else

INTER_FLAGS =

endif

# We compile the files in the library directory with --trace minimum by
# default (set in FLAGS), which has no effect in non-debugging grades and
# causes the library to be shallow traced, not deep traced, in debugging
# grades. This is probably what most users want, and it makes it much easier to
# maintain the expected output of the debugging test cases in debugging grades.
# However, this can be overridden by setting LIBRARY_TRACE_MINIMUM to no.
#
# Always generate dependencies for use by `mmc --make'.

ifeq ($(LIBRARY_TRACE_MINIMUM),no)
LIBRARY_TRACE_LEVEL = --trace default
else
LIBRARY_TRACE_LEVEL =
endif

# We need to compile the library with --strict-sequential for two reasons:
# (1) Otherwise Mercury code that is compiled with --strict-sequential
#     might do the wrong thing, because the standard library wasn't compiled
#     with --strict-sequential.  (We could make it a separate grade, but
#     that's not worth it.)
# (2) The code for get_determinism in library/exception.m relies on it
#     (in particular it relies on --no-reorder-disj).

MCFLAGS += --flags LIB_FLAGS $(CONFIG_OVERRIDE)
MCFLAGS += $(LIBRARY_TRACE_LEVEL) $(INTER_FLAGS)

# The IL, C#, Java and Erlang implementations of the standard library are not
# yet complete, so we need to pass `--allow-stubs' to get them to compile.
# Since the standard library is compiled with `--halt-at-warn',
# we also need `--no-warn-stubs'.
ifneq ("$(filter il% csharp% java% erlang%,$(GRADE))","")
MCFLAGS += --allow-stubs --no-warn-stubs
endif

#-----------------------------------------------------------------------------#

CFLAGS	     += $(DLL_CFLAGS)
ifeq ($(INSTALLABLE_PREFIX),yes)
MLFLAGS      += -R$(FINAL_INSTALL_MERC_LIB_DIR) \
		-R$(FINAL_INSTALL_MERC_GC_LIB_DIR)
MCFLAGS      += -R$(FINAL_INSTALL_MERC_LIB_DIR) \
		-R$(FINAL_INSTALL_MERC_GC_LIB_DIR)
endif

# Let javac find jmercury/runtime/*.java files.
JAVACFLAGS   += -sourcepath .

# -Xmx256m doesn't always seem to be enough memory to build the standard
# library. This bumps up the memory when building the standard library
# if the javac executable accepts the -J-Xmx flag, without bumping up
# the memory requirements in general.
ifneq ("$(findstring -J-Xmx,$(JAVACFLAGS))","")
JAVACFLAGS   += -J-Xmx512m
endif

MTAGS	= $(SCRIPTS_DIR)/mtags

LN	= ln

#-----------------------------------------------------------------------------#

# Set the install name for Darwin shared libraries.  We disable the
# --shlib-linker-use-install-name mmc option so that the -install_name linker
# option is not passed in the .dep files.  We do this to avoid a problem when
# building from the C source distribution:  if the C source distribution is
# generated on a non-Darwin system then the -install_name option is not passed
# in the .dep files, so it must be passed here, however if a C source
# distribution is generated on a Darwin system then by default the
# -install_name option will be passed in the .dep files which will cause it to
# be passed twice (here and in the .dep files) which is not allowed by the
# linker, so we disable the mmc option which causes the -install_name option
# to be passed in the .dep files.

LD_LIBFLAGS-libmer_std.dylib = -install_name \
	$(FINAL_INSTALL_MERC_LIB_DIR)/libmer_std.dylib

#-----------------------------------------------------------------------------#

# Stuff for Windows DLLS using gnu-win32

ifeq ($(USE_DLLS),yes)

DLL_CFLAGS = -Dlib$(STD_LIB_NAME)_DEFINE_DLL

include $(MERCURY_DIR)/Makefile.DLLs

else

DLL_CFLAGS =
DLL_DEF_LIB =

endif

#-----------------------------------------------------------------------------#

# targets

.PHONY: all
all:	mercury all-ints $(TAGS_FILE_EXISTS)

.PHONY: mercury
mercury: lib_std

#-----------------------------------------------------------------------------#

.PHONY: depend
depend:	LIB_FLAGS copy_java_runtime_files $(STD_LIB_NAME).depend

.PHONY: check
check:	$(STD_LIB_NAME).check

.PHONY: all-ints

ifeq ($(LIBRARY_INTERMODULE),yes)

all-ints: int3s ints opts trans_opts

else

all-ints: int3s ints

endif

.PHONY: int3s
int3s:	$(STD_LIB_NAME).int3s

.PHONY: ints
ints:	$(STD_LIB_NAME).ints

.PHONY: opts
opts:	$(STD_LIB_NAME).opts

.PHONY: trans_opts
trans_opts: $(STD_LIB_NAME).trans_opts
$(STD_LIB_NAME).trans_opts: $($(STD_LIB_NAME).trans_opts)

#-----------------------------------------------------------------------------#

tags:	$(MTAGS) $($(STD_LIB_NAME).ms)
	$(MTAGS) $($(STD_LIB_NAME).ms)

.PHONY: tags_file_exists
tags_file_exists:
	@if test ! -f tags; then echo making tags; \
	$(MTAGS) $($(STD_LIB_NAME).ms); \
	fi

$(STD_LIB_NAME).stats: $(COMPILER_DIR)/source_stats.awk $($(STD_LIB_NAME).ms)
	awk -f $(COMPILER_DIR)/source_stats.awk \
		`vpath_find $($(STD_LIB_NAME).ms)` > $@

#-----------------------------------------------------------------------------#

.PHONY: dates
dates:
	touch $($(STD_LIB_NAME).dates)

#-----------------------------------------------------------------------------#

# Note that we need to explicitly mention $($(STD_LIB_NAME).pic_ss) somewhere
# in the Mmakefile, otherwise it won't build properly with --target asm:
# GNU Make's pattern rule algorithm will try to use the .m -> .c -> .pic_o
# rule chain rather than the .m -> .pic_s_date -> .pic_s -> .pic_o chain.
# So don't remove the pic_ss target here.

.PHONY: os cs ss pic_ss ils javas
ifneq ($(MMAKE_USE_MMC_MAKE),yes)
os: $($(STD_LIB_NAME).os)
cs: $($(STD_LIB_NAME).cs)
ss: $($(STD_LIB_NAME).ss)
pic_ss: $($(STD_LIB_NAME).pic_ss)
ils: $($(STD_LIB_NAME).ils)
javas: $($(STD_LIB_NAME).javas)
else
os: $(STD_LIB_NAME).os
cs: $(STD_LIB_NAME).cs
ss: $(STD_LIB_NAME).ss
pic_ss: $(STD_LIB_NAME).pic_ss
ils: $(STD_LIB_NAME).ils
javas: $(STD_LIB_NAME).javas
endif

# javac expects to find the sources for symbols named jmercury.runtime.* in
# jmercury/runtime/*, but in our sources those symbols actually come from
# java/runtime/*.  So we set up a symbolic link to help javac find the way to
# the sources.
.PHONY: copy_java_runtime_files
.PHONY: install_mer_rt

ifeq ("$(findstring java,$(GRADE))","java")

copy_java_runtime_files:
	[ -d jmercury ] || mkdir jmercury
	[ -d jmercury/runtime ] || cp -r ../java/runtime jmercury

install_mer_rt: $(RT_LIB_NAME).jar
	mkdir -p $(INSTALL_PREFIX)/lib/mercury/lib/$(GRADE)
	cp $(RT_LIB_NAME).jar $(INSTALL_PREFIX)/lib/mercury/lib/$(GRADE)

else

copy_java_runtime_files:

install_mer_rt:

endif

# mmc --make must be used to install the java grade.
ifeq ($(MMAKE_USE_MMC_MAKE),yes)
$(STD_LIB_NAME).jar: libmer_std
endif

$(RT_LIB_NAME).jar: copy_java_runtime_files
	$(JAVAC) $(ALL_JAVACFLAGS) jmercury/runtime/*.java
	$(JAR) $(JAR_CREATE_FLAGS) $(RT_LIB_NAME).jar jmercury/runtime/*.class
	$(JAR) i $(RT_LIB_NAME).jar
	# -+cd jmercury/runtime && mmake $(NATIVE_SO)
	# -cp jmercury/runtime/$(NATIVE_SO) .

# This shared object was used to implement some standard library methods,
# but currently not.
NATIVE_SO = Native.$(EXT_FOR_SHARED_LIB)

#-----------------------------------------------------------------------------#

.PHONY: lib_std

# The ilc and il grades target Microsoft's .NET frameworks.
ifeq ($(findstring il,$(GRADE)),il)

lib_std: mercury.dll lib$(STD_LIB_NAME)

# We have to hardcode the names of the runtime DLL files.
RUNTIME_DLLS=mercury_dotnet.dll mercury_il.dll

CSCFLAGS=/t:module

# If you do generate a new strong name, you had better update
# compiler/mlds_to_il.m to generate references to it.  It is also hard-coded
# in runtime/mercury_il.il.
# This is hard-coding is sub-optimal -- the compiler should automatically find
# out what the strong name is.
library_strong_name.sn:
	sn -k library_strong_name.sn

mercury.dll: $($(STD_LIB_NAME).dlls) $($(STD_LIB_NAME).foreign_dlls) \
		$(RUNTIME_DLLS) library_strong_name.sn
	$(MS_AL) -v:0.0.0.0 -keyf:library_strong_name.sn -out:mercury.dll \
		$($(STD_LIB_NAME).dlls) $($(STD_LIB_NAME).foreign_dlls) \
		$(RUNTIME_DLLS)

# This is useful for running PEverify on the library.
# For multi-file assemblies, PEverify only works on executables,
# not on libraries.
mercury_exe.exe: $($(STD_LIB_NAME).dlls) $($(STD_LIB_NAME).foreign_dlls) \
		$(RUNTIME_DLLS) library_strong_name.sn
	$(MS_AL) -v:0.0.0.0 -keyf:library_strong_name.sn -out:mercury_exe.exe \
		$($(STD_LIB_NAME).dlls) $($(STD_LIB_NAME).foreign_dlls) \
		$(RUNTIME_DLLS)

# The runtime DLLs must be copied into this directory from the runtime
# directory so they can be installed properly.
.PHONY: copy_runtime_dlls
copy_runtime_dlls: $(RUNTIME_DLLS)

mercury_il.dll: ../runtime/mercury_il.dll
	cp ../runtime/mercury_il.dll .

mercury_dotnet.dll: ../runtime/mercury_dotnet.dll
	cp ../runtime/mercury_dotnet.dll .

CSHARP_MODULES = \
	array \
	builtin \
	char \
	construct \
	dir \
	exception \
	float \
	int \
	io \
	library \
	math \
	private_builtin \
	rtti_implementation \
	std_util \
	string \
	time \
	type_desc

CSHARP_DLLS = $(CSHARP_MODULES:%=%__csharp_code.dll)

$(CSHARP_DLLS) : $(RUNTIME_DLLS)

endif

#-----------------------------------------------------------------------------#

# For C# we include the runtime module directly into mer_std.dll.
ifneq ("$(filter csharp%,$(GRADE))","")
LINK_LIB_OPTS :=
# Don't use Unix style paths with the Microsoft C# compiler.
ifeq ($(CSC),csc)
MLOBJS += ..\runtime\mercury_dotnet.cs
else
MLOBJS += ../runtime/mercury_dotnet.cs
endif
endif

#-----------------------------------------------------------------------------#

ifeq ($(MMAKE_USE_MMC_MAKE),yes)
ifeq ("$(findstring csharp,$(GRADE))","csharp")
$(STD_LIB_NAME).dll: libmer_std
lib_std: $(STD_LIB_NAME).dll
endif
ifeq ("$(findstring java,$(GRADE))","java")
lib_std: $(STD_LIB_NAME).jar $(RT_LIB_NAME).jar
endif
ifeq ("$(findstring erlang,$(GRADE))","erlang")
lib_std: $(STD_LIB_NAME).beams
endif
endif

#-----------------------------------------------------------------------------#
#
# Some extra rules that we need for the C and asm back-ends
#
ifeq ("$(filter il% csharp% java% erlang%,$(GRADE))","")

# The following dependency is just there to improve compilation speed;
# making tree234.$O first improves effective parallelism with parallel makes.
# `mmc --make' does not support parallel makes, so this dependency just
# slows things down.
ifneq ($(MMAKE_USE_MMC_MAKE),yes)
lib_std: $(os_subdir)tree234.$O
endif
lib_std: lib$(STD_LIB_NAME)

ifneq ($(MMAKE_USE_MMC_MAKE),yes)

EXTRA_INIT_COMMAND = ./print_extra_inits $($(STD_LIB_NAME).ms)

else

MCFLAGS += --extra-init-command ./print_extra_inits

endif

endif	# GRADE != il && GRADE != csharp && GRADE != java && GRADE != erlang

#-----------------------------------------------------------------------------#

# Rebuild all the object files if the configuration macros or VERSION
# have changed. Only some source files use the configuration macros,
# but these uses may leak into other object files with inter-module
# optimization.
$(mer_std.os): $(RUNTIME_DIR)/mercury_conf.h

# The object files in this directory depend on many of the header files
# in the runtime. However, changes to many of these header files require
# a global make clean. Here we list only the header files from the runtime
# whose changes don't usually require a make clean but which nevertheless
# require some files in the library to be recompiled.
$(os_subdir)benchmarking.$O \
$(os_subdir)benchmarking.pic_o \
$(os_subdir)construct.$O \
$(os_subdir)construct.pic_o \
$(os_subdir)deconstruct.$O \
$(os_subdir)deconstruct.pic_o \
$(os_subdir)std_util.$O \
$(os_subdir)std_util.pic_o \
	: ../runtime/mercury_stack_layout.h

$(os_subdir)deconstruct.$O \
$(os_subdir)deconstruct.pic_o \
$(os_subdir)std_util.$O \
$(os_subdir)std_util.pic_o \
	: ../runtime/mercury_ml_functor_body.h \
	../runtime/mercury_ml_arg_body.h \
	../runtime/mercury_ml_deconstruct_body.h \
	../runtime/mercury_deconstruct_macros.h \
	../runtime/mercury_deconstruct.h

$(os_subdir)construct.$O \
$(os_subdir)construct.pic_o \
$(os_subdir)std_util.$O \
$(os_subdir)std_util.pic_o \
	: ../runtime/mercury_construct.h

$(os_subdir)type_desc.$O \
$(os_subdir)type_desc.pic_o \
$(os_subdir)std_util.$O \
$(os_subdir)std_util.pic_o \
	: ../runtime/mercury_type_desc.h

$(os_subdir)table_builtin.$O \
$(os_subdir)table_builtin.pic_o \
	: ../runtime/mercury_tabling_macros.h \
	../runtime/mercury_tabling_preds.h \
	../runtime/mercury_minimal_model.h

$(os_subdir)par_builtin.$O \
$(os_subdir)par_builtin.pic_o \
	: ../runtime/mercury_context.h \
	../runtime/mercury_par_builtin.h \
	../runtime/mercury_thread.h

#-----------------------------------------------------------------------------#

# In the past we generated liblibrary.* and then linked
# libmer_std.* to the files.
realclean_local:
	rm -f mercury_dotnet.dll mercury_il.dll
	rm -f liblibrary.$A liblibrary.so library.init
	rm -f $($(STD_LIB_NAME).mods:%=%.h)
	rm -f tags LIB_FLAGS LIB_FLAGS.date
	rm -f runtime
	rm -rf jmercury
	rm -f mr_int.class mr_float.class mr_char.class
	rm -f mr_int\$$*.class mr_float\$$*.class mr_char\$$*.class
	rm -f $(STD_LIB_NAME).jar $(RT_LIB_NAME).jar $(NATIVE_SO)
	rm -f erlang_conf.hrl

#-----------------------------------------------------------------------------#

ifeq ($(LIBRARY_INTERMODULE),yes)

# Installation targets

# LIBRARY_INTERMODULE should be turned off only during development.
# A full install requires the library's .opt and .trans_opt files.

# If you add a new target below, please add a parallel target for the
# case LIBRARY_INTERMODULE != yes.

.PHONY: install
install: install_mercury

.PHONY: install_all
install_all: install_mercury

ifneq ($(MMAKE_USE_MMC_MAKE),yes)

.PHONY: install_mercury
install_mercury: install_ints install_hdrs install_library

.PHONY: install_ints
install_ints: lib$(STD_LIB_NAME).install_ints

.PHONY: install_hdrs
install_hdrs: lib$(STD_LIB_NAME).install_hdrs

ifneq (,$(findstring il,$(GRADE)))

# XXX some modules in the browser directory need library.int3

.PHONY: install_library
install_library: mercury.dll install_grade_dirs install_gac \
		install_strong_name lib$(STD_LIB_NAME).install_grade_hdrs \
		lib$(STD_LIB_NAME).install_opts
	cp `vpath_find $($(STD_LIB_NAME).foreign_dlls) $($(STD_LIB_NAME).dlls) \
		mercury.dll` $(INSTALL_MERC_LIB_DIR)

# To build the .NET library with debugging information, add
# the following to ../Mmake.params:
#
#	EXTRA_ILASMFLAGS=/debug
#	EXTRA_CSCFLAGS=/debug+ /debug:full
#
# XXX Unfortunately, however, this is a problem with AL, which prohibits
# linking debuggable C# code with IL code.  The following
# horrible hack avoids that problem.  After modifying Mmake.params as
# described above, invoke `mmake install_debug_library'.

.PHONY: install_debug_library
install_debug_library:
	-$(MMAKE) $(MMAKEFLAGS)
	$(MMAKE) $(MMAKEFLAGS) munge_dll_debug_info
	$(MMAKE) $(MMAKEFLAGS) install_library

# The GAC is the global assembly cache, which is the system managed
# place to put .NET assemblies.
# gacutil installs (and uninstalls) DLLs from the GAC.
.PHONY: install_gac
install_gac: mercury.dll
	gacutil -i mercury.dll

.PHONY: install_strong_name
install_strong_name: library_strong_name.sn
	cp `vpath_find library_strong_name.sn` \
		$(INSTALL_MERC_LIB_DIR)/mercury.sn

else

# we depend on lib$(STD_LIB_NAME) because lib$(STD_LIB_NAME).install_library
# doesn't make library.int3, but some modules in the browser directory need it.

.PHONY: install_library
install_library: lib$(STD_LIB_NAME) lib$(STD_LIB_NAME).install_library

endif  #ifneq (,$(findstring il,$(GRADE)))

else	#ifneq ($(MMAKE_USE_MMC_MAKE),yes)

.PHONY: install_mercury
install_mercury: install_library

.PHONY: install_library
install_library: install_mer_rt lib$(STD_LIB_NAME).install

endif	#ifneq ($(MMAKE_USE_MMC_MAKE),yes)

else

.PHONY: install
install:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

.PHONY: install_all
install_all:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

.PHONY: install_mercury
install_mercury:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

.PHONY: install_ints
install_ints:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

.PHONY: install_hdrs
install_hdrs:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

.PHONY: install_library
install_library:
	echo "Can't do make install without LIBRARY_INTERMODULE=yes"
	exit 1

endif

.PHONY: munge_dll_debug_info
munge_dll_debug_info:
	for file in *csharp_code.dll; do \
		ildasm /out:$$file.ild $$file; \
		sed  's/01 00 00 01/01 00 01 01/' $$file.ild \
			> $$file.ild.munged; \
		ilasm /quiet /dll /debug /out:$$file $$file.ild.munged; \
	done

#-----------------------------------------------------------------------------#
