##############################################
#  NB: This is the main Makefile for PRISM.  #
#      It calls all the other Makefiles in   #
#      subdirectories, passing in all the    #
#      options configured here.              #
##############################################

###############
# Directories #
###############

# Note that these are all relative to the PRISM directory
# to make the distribution more 'portable'.
# If this is a problem, the best solution is to create symlinks.

export PRISM_SRC_DIR     = src
export PRISM_OBJ_DIR     = obj
export PRISM_LIB_DIR     = lib
export PRISM_BIN_DIR     = bin
export PRISM_INCLUDE_DIR = include
export PRISM_IMAGES_DIR  = images
export PRISM_DTDS_DIR    = dtds

# Java locations, files

JAVA_SRC_DIR := src
JAVA_CLASSES_DIR := classes
JAVA_TESTS_SRC_DIR := unit-tests
JAVA_TESTS_CLASSES_DIR := $(JAVA_CLASSES_DIR)

JAVA_SRC_FILES := $(shell find $(JAVA_SRC_DIR) -name "*.java")
JAVA_SRC_FILES_LIST := $(JAVA_CLASSES_DIR)/java_sources.txt
JAVA_COMPILED_STAMP := $(JAVA_CLASSES_DIR)/java_compiled.stamp
JAVA_PARSER_JJ := $(JAVA_SRC_DIR)/parser/PrismParser.jj
JAVA_PARSER_JAVA := $(JAVA_SRC_DIR)/parser/PrismParser.java
JAVA_TESTS_SRC_FILES := $(shell find $(JAVA_TESTS_SRC_DIR) -name "*.java")
JAVA_TESTS_SRC_FILES_LIST := $(JAVA_TESTS_CLASSES_DIR)/java_tests_sources.txt
JAVA_TESTS_COMPILED_STAMP := $(JAVA_TESTS_CLASSES_DIR)/java_tests_compiled.stamp

# Location of CUDD (used to be variable; now mainly fixed with the git repo layout)

CUDD_DIR := ../cudd

####################
# Operating system #
####################

# OSTYPE needs to be one of: linux, solaris, cygwin, darwin
# This makefile will try to detect which one of these is appropriate.
# If this detection does not work, or you wish to override it,
# either uncomment one of the lines directly below
# or pass a value to make directly, e.g.: make OSTYPE=linux

#OSTYPE = linux
#OSTYPE = solaris
#OSTYPE = cygwin
#OSTYPE = darwin

ifneq ($(OSTYPE),)
	# Look for common variants, e.g. gnu-linux -> linux
	ifneq (,$(findstring linux, $(OSTYPE)))
	  OSTYPE := linux
	endif
	ifneq (,$(findstring solaris, $(OSTYPE)))
	  OSTYPE := solaris
	endif
	ifneq (,$(findstring cygwin, $(OSTYPE)))
	  OSTYPE := cygwin
	endif
	# For Cygwin , OSTYPE is sometimes set to "posix"
	ifneq (,$(findstring posix, $(OSTYPE)))
	  OSTYPE := cygwin
	endif
	ifneq (,$(findstring darwin, $(OSTYPE)))
	  OSTYPE := darwin
	endif
else
	# If OSTYPE is not defined/available,
	# First try OS (on Windows), otherwise try uname
	ifeq ($(OS),Windows_NT)
		OSTYPE := cygwin
	else
		UNAME_STR := $(shell uname -s 2>/dev/null || echo unknown)
		ifneq (,$(findstring Linux, $(UNAME_STR)))
			OSTYPE := linux
		endif
		ifneq (,$(findstring SunOS, $(UNAME_STR)))
			OSTYPE := solaris
		endif
		ifneq (,$(findstring CYGWIN, $(UNAME_STR)))
			OSTYPE := cygwin
		endif
		ifneq (,$(findstring MINGW, $(UNAME_STR)))
			OSTYPE := cygwin
		endif
		ifneq (,$(findstring Darwin, $(UNAME_STR)))
			OSTYPE := darwin
		endif
	endif
endif

################
# Architecture #
################

# For Linux/Mac, we use uname to check the architecture
ifeq ($(OSTYPE),linux)
	ifneq (,$(findstring 86_64, $(shell uname -m)))
		ARCH := amd64
	endif
	ifneq (,$(findstring ia64, $(shell uname -m)))
		ARCH := ia64
	endif
	ifneq (,$(findstring aarch64, $(shell uname -m)))
		ARCH := aarch64
	endif
endif
ifeq ($(OSTYPE),darwin)
	ifneq (,$(findstring x86_64, $(shell uname -m)))
		ARCH := x86_64
	endif
	ifneq (,$(findstring arm64, $(shell uname -m)))
		ARCH := arm64
	endif
endif
# For Windows, we decide whether to build in 64-bit mode based on
# whether java is 32/64-bit (since these need to match)
ifeq ($(OSTYPE),cygwin)
	# Default to 64-bit, which is most likely these days
	ARCH := x86_64
    ifeq ($(shell which java 2>/dev/null),)
        $(error "Could not find java executable. Please install Java or add it to your PATH.")
    endif
    JAVA_VERSION_STRING := $(shell java -version 2>&1)
	ifneq (,$(findstring 32-bit, $(JAVA_VERSION_STRING)))
        ARCH := 
    endif
    ifneq (,$(findstring Client VM, $(JAVA_VERSION_STRING)))
        ARCH := 
    endif
endif

##################
# Pre-processing #
##################

# Convert scripts needed for building, in case extracted with CRLF endings
PRE_PROC := $(shell if [ "$(OSTYPE)" = "cygwin" ]; then \
  dos2unix "$(PRISM_SRC_DIR)"/scripts/*.sh; \
  dos2unix "$(CUDD_DIR)"/setup.sh; \
fi 2> /dev/null)

########
# Java #
########

# JAVA_DIR needs to be set to the location of your Java installation.
# This makefile will try to detect this automatically based on the location of the javac command.
# If this detection does not work, or you wish to override it,
# either set the variable yourself by uncommenting and/or modifying one of the lines below
# or pass a value to make directly, e.g.: make JAVA_DIR=/usr/java

# Find javac
DETECT_JAVAC = $(shell $(PRISM_SRC_DIR)/scripts/findjavac.sh 2> /dev/null)

# Find directory containing javac
ifeq ("$(DETECT_JAVAC)","")
  JAVA_DIR :=
else
  JAVA_DIR := $(shell dirname "$(DETECT_JAVAC)" | sed -e 's/\/bin//' -e 's/\/Commands//')
endif

# As a backup way of detecting JAVA_DIR, run java_home
JAVA_DIR_BACKUP := $(shell \
	if [ -f /usr/libexec/java_home ]; then /usr/libexec/java_home; \
	else echo ""; fi )

#JAVA_DIR =	/usr/java
#JAVA_DIR =	/usr/java/j2sdk1.4.2
#JAVA_DIR =	/bham/java/packages/j2sdk1.4.2
#JAVA_DIR =	/cygdrive/c/java/j2sdk1.4.2
#JAVA_DIR =	/System/Library/Frameworks/JavaVM.framework

# Now we locate the JNI header files jni.h and jni_md.h
# (in fact this is the only reason we need JAVA_DIR)
JAVA_JNI_H_DIR := $(shell \
	if [ -f "$(JAVA_DIR)"/include/jni.h ]; then echo "$(JAVA_DIR)"/include; \
	elif [ -f "$(JAVA_DIR)"/Headers/jni.h ]; then echo "$(JAVA_DIR)"/Headers; \
	elif [ -f "$(JAVA_DIR_BACKUP)"/include/jni.h ]; then echo "$(JAVA_DIR_BACKUP)"/include; \
	elif [ -f "$(JAVA_DIR_BACKUP)"/Headers/jni.h ]; then echo "$(JAVA_DIR_BACKUP)"/Headers; \
	else echo ""; fi )
JAVA_JNI_MD_H_DIR := $(shell (ls "$(JAVA_JNI_H_DIR)"/jni_md.h "$(JAVA_JNI_H_DIR)"/*/jni_md.h | head -n 1 | sed 's/\/jni_md.h//') 2>/dev/null)
JAVA_INCLUDES := -I $(JAVA_JNI_H_DIR) -I $(JAVA_JNI_MD_H_DIR)

##################
# Compilers etc. #
##################

ifeq ($(OSTYPE),cygwin)
	ifeq ($(ARCH),x86_64)
		CC := /usr/bin/x86_64-w64-mingw32-gcc
		CXX := /usr/bin/x86_64-w64-mingw32-g++
	else
		CC := /usr/bin/i686-w64-mingw32-gcc
		CXX := /usr/bin/i686-w64-mingw32-g++
	endif
	JAVACC := javacc.bat
else
	CC := gcc
	CXX := g++
	JAVACC := javacc
endif
LD := $(CXX)
JAVAC := javac
JAVA := java

export CC CXX LD JAVAC JAVACC

##############
# Flags etc. #
##############

# Tell compiler to generate debug information?
# (WARNING: must not contain a % symbol!)
DEBUG := 
#DEBUG := -g

# Compiler optimisation level:
# (WARNING: must not contain a % symbol!)
OPTIMISE := -O3
#OPTIMISE :=

# Compiler warnings to enable:
# (WARNING: must not contain a % symbol!)
WARNINGS := #-Wformat

# Flags for compilation/linking
# Flags to generate shared libraries
# Executable/library naming conventions
# Suffix for binary distribution directory
# (requires GNU make for conditional evaluation)

# Linux
ifeq ($(OSTYPE),linux)
	ifeq ($(ARCH),amd64)
		# Position Independent Code required on AMD64/Itanium
		CFLAGS := -m64 -fPIC -DPIC $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS :=
		BINDISTSUFFIX := linux64
		BINDISTARCH := x86
	else
	ifeq ($(ARCH),ia64)
		# Position Independent Code required on AMD64/Itanium
		# Note: We omit the -m64 flag from here since it seems to be unsupported by gcc on IA64
		CFLAGS := -fPIC -DPIC $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS :=
		BINDISTSUFFIX := linux64
		BINDISTARCH := ia64
	else
	ifeq ($(ARCH),aarch64)
		# Position Independent Code required on Aarch64
		CFLAGS := -fPIC -DPIC $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS :=
		BINDISTSUFFIX := linux64
		BINDISTARCH := arm
	else
		CFLAGS := -m32 $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS :=
		BINDISTSUFFIX := linux32
		BINDISTARCH := x86
	endif
	endif
	endif
	BIN_TARGETS := prism.linux xprism.linux
	JFLAGS := -encoding UTF8
	SHARED := -shared
	#SHARED := -G
	EXE :=
	LIBPREFIX := lib
	LIBSUFFIX := .so
	LIBMATH := -lm
	CLASSPATHSEP := :
endif
# Solaris
ifeq ($(OSTYPE),solaris)
	CFLAGS := -mcpu=ultrasparc $(DEBUG) $(OPTIMISE) $(WARNINGS)
	CXXFLAGS := --std=c++11 $(CFLAGS)
	LDFLAGS :=
	BINDISTSUFFIX := solaris
	BINDISTARCH := solaris
	BIN_TARGETS := prism.linux xprism.linux
	JFLAGS := -encoding UTF8
	SHARED := -shared -mimpure-text
	EXE :=
	LIBPREFIX := lib
	LIBSUFFIX := .so
	LIBMATH := -lm
	CLASSPATHSEP := :
endif
# Cygwin
ifeq ($(OSTYPE),cygwin)
	# -DWIN32 needed for lpsolve (included e.g. in sparse) - WIN32 is only defined by mingw/gcc without --std=c++11
	# -static-libgcc -static-libstdc+ needed to build binaries that don't rely on these libraries to be present
	# -Wl,--add-stdcall-alias needed so shared libraries can be read from JNI
	# -Wl ... -lpthread ... --no-whole-archive needed to statically include pthread in case missing
	ifeq ($(ARCH),x86_64)
		CFLAGS := $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 -DWIN32 $(CFLAGS)
		LDFLAGS := -static-libgcc -static-libstdc++ -Wl,--add-stdcall-alias -Wl,-Bstatic,--whole-archive -lpthread -Wl,-Bdynamic,--no-whole-archive
		BINDISTSUFFIX := win64
		BINDISTARCH := x86
	else
		CFLAGS := -march=i686 $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 -DWIN32 $(CFLAGS)
		LDFLAGS := -static-libgcc -static-libstdc++ -Wl,--add-stdcall-alias -Wl,-Bstatic,--whole-archive -lpthread -Wl,-Bdynamic,--no-whole-archive
		BINDISTSUFFIX := win32
		BINDISTARCH := x86
	endif
	BIN_TARGETS := prism.cygwin xprism.linux prism.bat.win xprism.bat.win
	JFLAGS := -encoding UTF8
	SHARED := -shared
	#SHARED := -G
	EXE := .exe
	LIBPREFIX :=
	LIBSUFFIX := .dll
	LIBMATH := 
	CLASSPATHSEP := ;
endif
# Darwin
ifeq ($(OSTYPE),darwin)
	ifeq ($(ARCH),x86_64)
		CFLAGS := -arch x86_64 -fPIC -DPIC $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS := -arch x86_64 -Wl,-search_paths_first
		BINDISTSUFFIX := mac64
		BINDISTARCH := x86
		BIN_TARGETS := prism.darwin xprism.linux
	else
	ifeq ($(ARCH),arm64)
		CFLAGS := -arch arm64 -fPIC -DPIC $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS := -arch arm64 -Wl,-search_paths_first
		BINDISTSUFFIX := mac64
		BINDISTARCH := arm
		BIN_TARGETS := prism.darwin xprism.linux
	else
		CFLAGS := -arch i386 $(DEBUG) $(OPTIMISE) $(WARNINGS)
		CXXFLAGS := --std=c++11 $(CFLAGS)
		LDFLAGS := -arch i386 -Wl,-search_paths_first
		BINDISTSUFFIX := mac32
		BINDISTARCH := x86
		BIN_TARGETS := prism.darwin xprism.linux
	endif
	endif
	JFLAGS := -encoding UTF8
	SHARED := -dynamiclib
	EXE :=
	LIBPREFIX := lib
	LIBSUFFIX := .dylib
	LIBMATH := -lm
	CLASSPATHSEP := :
endif

# Java flags
JFLAGS += -proc:none # Remove annotation processing warnings due to log4j
JFLAGS += --release 11 # Enforce Java language level

export CFLAGS CXXFLAGS LDFLAGS JFLAGS LIBPREFIX LIBSUFFIX

##########################################
# Main part of Makefile: Compiling PRISM #
##########################################

EXT_PACKAGES := lpsolve55 lp_solve_5.5_java

.PHONY: clean javadoc tests release

# inhibit building in parallel (-j option)
.NOTPARALLEL:

default: all

all: cuddpackage extpackages prism

# Build our customised version of CUDD
# (let CUDD choose its own linker - problems on Cygwin if not)
# (should be no need for linker options - we use static CUDD libraries)
# (note CFLAGS/CXXFLAGS _are_ passed on, e.g. to preserve PIC consistency)
cuddpackage: checks
	@echo Making cudd ...; cd $(CUDD_DIR) && \
	LD= LDFLAGS= ./install.sh

# Build various external libraries needed by PRISM
extpackages: checks
	@for ext in $(EXT_PACKAGES); do \
	  echo Making $$ext ...; \
	  (cd ext/$$ext && \
	  $(MAKE) \
	  OSTYPE="$(OSTYPE)" \
	  ARCH="$(ARCH)" \
	  SHARED="$(SHARED)" \
	  LIBMATH="$(LIBMATH)" \
	  BINDISTSUFFIX="$(BINDISTSUFFIX)" \
	  BINDISTARCH="$(BINDISTARCH)" \
	  JAVA_DIR="$(JAVA_DIR)" \
	  JAVA_JNI_H_DIR="$(JAVA_JNI_H_DIR)" \
	  JAVA_JNI_MD_H_DIR="$(JAVA_JNI_MD_H_DIR)" \
	  ) || exit 1; \
	done

# Compile main PRISM code
# (we also do some preparatory checks, and build launch scripts afterwards)
prism: checks prism_java prism_lib prism_ng bin_scripts

# Compile all Java code, build JNI headers
prism_java: $(JAVA_COMPILED_STAMP)

$(JAVA_COMPILED_STAMP): $(JAVA_SRC_FILES)
	@mkdir -p $(JAVA_CLASSES_DIR) $(PRISM_INCLUDE_DIR)/jni
	@find $(JAVA_SRC_DIR) -name "*.java" > $(JAVA_SRC_FILES_LIST)
	@echo "Compiling Java files..."
	$(JAVAC) $(JFLAGS) -classpath "$(PRISM_LIB_DIR)/*" -d $(JAVA_CLASSES_DIR) -h $(PRISM_INCLUDE_DIR)/jni @$(JAVA_SRC_FILES_LIST)
	@touch $@

# Rebuild PRISM (JavaCC) compiler if needed
# Just emit warning if javacc not available and fall back to pre-compiled .java
$(JAVA_PARSER_JAVA): $(JAVA_PARSER_JJ)
	@$(JAVACC) $< || echo "Warning: Did not recompile $@"

# Use this to force a rebuild (with javacc) of the main parser
parser: parser_force prism_java

parser_force:
	@ echo "Forcing parser rebuild"
	@$(JAVACC) $(JAVA_PARSER_JJ)

# On Windows, convert the generated JNI headers using dos2unix
# and any scripts to be run from Cywgin, in case extracted with CRLF endings.
	@if [ "$(OSTYPE)" = "cygwin" ]; then \
	  dos2unix $(PRISM_INCLUDE_DIR)/jni/*.h; \
	  find .. -name '*.sh' -exec dos2unix {} \; ; \
	  dos2unix "$(PRISM_SRC_DIR)"/bin/prism.cygwin; \
	fi

PRISM_LIB_DIRS := dd jdd odd dv prism mtbdd sparse hybrid

PRISM_LIB_EXCLUDE_FILES := $(PRISM_SRC_DIR)/dd/dd_test.cc
PRISM_LIB_ALL_SRC_FILES := $(shell find $(foreach d,$(PRISM_LIB_DIRS),$(PRISM_SRC_DIR)/$(d)) -name "*.cc")
PRISM_LIB_SRC_FILES := $(filter-out $(PRISM_LIB_EXCLUDE_FILES), $(PRISM_LIB_ALL_SRC_FILES))
PRISM_LIB_OBJ_FILES := $(patsubst $(PRISM_SRC_DIR)/%.cc, $(PRISM_OBJ_DIR)/%.o, $(PRISM_LIB_SRC_FILES))

OLD_SHARED_LIBS := dd jdd odd dv prismmtbdd prismsparse prismhybrid
OLD_SHARED_LIB_FILES := $(patsubst %, $(PRISM_LIB_DIR)/$(LIBPREFIX)%$(LIBSUFFIX), $(OLD_SHARED_LIBS))

INCLUDES := \
-I$(CUDD_DIR)/include \
-I"$(JAVA_JNI_H_DIR)" \
-I"$(JAVA_JNI_MD_H_DIR)" \
-I$(PRISM_INCLUDE_DIR) \
-Iext/lpsolve55/include

LIBRARIES := \
-L$(PRISM_LIB_DIR) \
-L$(CUDD_DIR)/lib \
-lcudd \
$(LIBMATH) \
-llpsolve55

# Setup RPATH. Use = here (not :=) so $@ is evaluated inside the recipe scope
RPATH_LINUX   = -Wl,-soname,$(notdir $@) -Wl,-rpath,'$$ORIGIN'
RPATH_DARWIN  = -Wl,-install_name,@rpath/$(notdir $@) -Wl,-rpath,@loader_path/.
# Then pick the right one based on OSTYPE
$(PRISM_LIB_DIR)/$(LIBPREFIX)prism$(LIBSUFFIX): RPATH_SETTING = \
    $(if $(filter linux,$(OSTYPE)),$(RPATH_LINUX), \
    $(if $(filter darwin,$(OSTYPE)),$(RPATH_DARWIN),))

# Compile the main native shared library
prism_lib: prism_java $(PRISM_LIB_DIR)/$(LIBPREFIX)prism$(LIBSUFFIX)

$(PRISM_LIB_DIR)/$(LIBPREFIX)prism$(LIBSUFFIX): $(PRISM_LIB_OBJ_FILES)
	@echo "Linking shared library $@..."
	$(LD) $(SHARED) $(LDFLAGS) $(RPATH_SETTING) -o $@ $^ $(LIBRARIES)

$(PRISM_OBJ_DIR)/%.o: $(PRISM_SRC_DIR)/%.cc
	@mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS) -c $< -o $@ -I$(PRISM_INCLUDE_DIR)/jni $(INCLUDES)

# Compile ngprism
# (on Windows, link winsocket library, ws2_32)
prism_ng: $(PRISM_OBJ_DIR)/prism/ngprism$(EXE)

$(PRISM_OBJ_DIR)/prism/ngprism$(EXE): $(PRISM_SRC_DIR)/prism/ngprism.c
	@echo "Bilding $@..."
	@if [ "$(EXE)" = "" ]; then \
	  $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $<; \
	else \
		$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $< -lws2_32; \
	fi;

# Compile (Java) unit tests
$(JAVA_TESTS_COMPILED_STAMP): $(JAVA_TESTS_SRC_FILES)
	@mkdir -p $(JAVA_TESTS_CLASSES_DIR)
	@find $(JAVA_TESTS_SRC_DIR) -name "*.java" > $(JAVA_TESTS_SRC_FILES_LIST)
	@echo "Compiling Java unit tests..."
	$(JAVAC) $(JFLAGS) -sourcepath $(JAVA_SRC_DIR) -classpath $(PRISM_CLASSPATH) -d $(JAVA_TESTS_CLASSES_DIR) -h $(PRISM_INCLUDE_DIR)/jni @$(JAVA_TESTS_SRC_FILES_LIST)
	@touch $@

# Copy/modify the launch scripts and put in the bin directory
bin_scripts:
	@mkdir -p $(PRISM_BIN_DIR)
	@for target in $(BIN_TARGETS); do \
	  target_trunc=`echo $$target | sed 's/\.[^.]*$$//'` && \
	  echo Copying "$(PRISM_SRC_DIR)/bin/$$target -> $(PRISM_BIN_DIR)/$$target_trunc" && \
	  cp $(PRISM_SRC_DIR)/bin/$$target "$(PRISM_BIN_DIR)/$$target_trunc"; \
	done;
	@echo Copying "$(PRISM_OBJ_DIR)/prism/ngprism$(EXE) -> $(PRISM_BIN_DIR)/ngprism$(EXE)" && \
	cp $(PRISM_OBJ_DIR)/prism/ngprism$(EXE) $(PRISM_BIN_DIR)/ngprism$(EXE)
	@./install.sh silent

# Some checks to make sure that the main settings are valid
checks:
	@(if [ "$(OSTYPE)" != "linux" -a "$(OSTYPE)" != "solaris" -a "$(OSTYPE)" != "cygwin" -a "$(OSTYPE)" != "darwin" ]; then \
	  echo "\033[33mTo compile PRISM, the environment variable OSTYPE"; \
	  echo "must be set to one of: linux, solaris, cygwin or darwin,"; \
	  echo "depending on which operating system you are using."; \
	  echo "This is not the case on your system. Please specify"; \
	  echo "the value of OSTYPE manually to make, e.g.:"; \
	  echo; \
	  echo "  make OSTYPE=linux"; \
	  echo; \
	  echo "Alternatively, if you wish, you can set the environment"; \
	  echo "variable yourself (using setenv or export) or you"; \
	  echo "can edit the value of OSTYPE directly in the Makefile."; \
	  echo "\033[0m"; \
	  exit 1; \
	fi; \
	if [ "$(JAVA_DIR)" = "" ]; then \
	  echo "\033[33mPRISM was unable to find the directory which contains"; \
	  echo "your Java distribution. Please specify this manually to"; \
	  echo "make, as in these examples:"; \
	  echo; \
	  echo "  make JAVA_DIR=/usr/java/j2sdk1.4.2"; \
	  echo "  make JAVA_DIR=\"/cygdrive/c/Program Files/Java/jdk1.4.2\""; \
	  echo; \
	  echo "See the PRISM manual for further information."; \
	  echo; \
	  echo "Alternatively, if you wish, you can set the environment"; \
	  echo "variable yourself (using setenv or export) or you"; \
	  echo "can edit the value of JAVA_DIR directly in the Makefile."; \
	  echo "\033[0m"; \
	  exit 1; \
	fi; \
	if [ ! -d "$(JAVA_DIR)" ]; then \
	  echo "\033[33mJava directory \"$(JAVA_DIR)\" does not exist."; \
	  echo "\033[0m"; \
	  exit 1; \
	fi; \
	if [ ! -f "$(JAVA_JNI_H_DIR)"/jni.h ]; then \
	  echo "\033[33mCould not locate JNI header jni.h within \"$(JAVA_DIR)\"."; \
	  echo "You may need to set JAVA_DIR by hand. See the PRISM manual for details."; \
	  echo "\033[0m"; \
	  exit 1; \
	fi; \
	if [ ! -f "$(JAVA_JNI_MD_H_DIR)"/jni_md.h ]; then \
	  echo "\033[33mCould not locate JNI header jni_md.h within \"$(JAVA_DIR)\"."; \
	  echo "You may need to set JAVA_DIR by hand. See the PRISM manual for details."; \
	  echo "\033[0m"; \
	  exit 1; \
	fi; \
	echo "VERSION: $(VERSION)"; \
	echo "OSTYPE/ARCH: $(OSTYPE) $(ARCH)"; \
	echo "JAVA_DIR: $(JAVA_DIR)"; \
	echo "JAVA_DIR_BACKUP: $(JAVA_DIR_BACKUP)"; \
	echo "JAVAC (which): "`which $(JAVAC)`; \
	echo "JAVAC (version): "$(shell $(JAVAC) -version) \
	)

# Misc: count the number of lines of code
count_loc:
	find $(PRISM_SRC_DIR) $(JAVA_SRC_DIR) -name '*.java' -o -name '*.cc' | xargs wc -l

###########
# Testing #
###########

# Run all unit tests
JUNIT_JAR = $(wildcard $(PRISM_LIB_DIR)/junit-platform-console-standalone*.jar)
unittests: $(JAVA_TESTS_COMPILED_STAMP)
	# Provide Regex to match our test classes. If none is given, only certain test classes are excluded by default.
	$(JAVA) -jar $(JUNIT_JAR) -cp classes --include-classname '^(Test.*|.+[.$$]Test.*|.+Tests?[.$$].+|.*Tests?)$$' -scan-classpath --details=summary

# Run a single test case from the test suite (useful quick check that the build was ok)
test:
	$(PRISM_BIN_DIR)/prism etc/tests/dtmc_pctl.prism etc/tests/dtmc_pctl.prism.props -h -test

# Run a single test case from the test suite that relies on lpsolve being properly installed
testlpsolve:
	$(PRISM_BIN_DIR)/prism etc/tests/test_lpsolve_mdpmo.prism etc/tests/test_lpsolve_mdpmo.prism.props -lp -test

# Run all tests from the test suite (in ../prism-tests and ./tests)
# Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
tests: testslocal
	@if [ -d ../prism-tests ]; then \
	  etc/scripts/prism-auto -t -m ../prism-tests -p $(PRISM_BIN_DIR)/prism --print-failures --nailgun --ngprism $(PRISM_BIN_DIR)/ngprism $(TESTS_ARGS); \
	else \
	  echo "Skipping tests"; \
	fi

# Just display the command to run the test suite on this version of PRISM
# Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
testsecho:
	@echo etc/scripts/prism-auto -t -m ../prism-tests -p $(PRISM_BIN_DIR)/prism --print-failures --nailgun --ngprism $(PRISM_BIN_DIR)/ngprism $(TESTS_ARGS)

# Run local tests (in ./tests)
# Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
testslocal:
	@if [ -d tests ]; then \
	  etc/scripts/prism-auto -t -m tests -p $(PRISM_BIN_DIR)/prism --print-failures --nailgun --ngprism $(PRISM_BIN_DIR)/ngprism $(TESTS_ARGS); \
	else \
	  echo "Skipping local tests"; \
	fi

# Run the tests from ../prism-tests (with different engine settings, picked up from ../prism-tests/all-engines.args)
# - Export tests are disabled, as there is currently no robust test mechanism for dealing with the
#   variations in the output of the different engines.
# - We run with --test-all, as failures for some engines should not abort the tests
# - We run with a timeout of 1 minute, as some engines take a long time for some properties
testsfull:
	etc/scripts/prism-auto -t -m ../prism-tests \
	--skip-export-runs --skip-duplicate-runs --test-all -a ../prism-tests/all-engines.args --timeout 1m \
	-p $(PRISM_BIN_DIR)/prism --print-failures --nailgun $(TESTS_ARGS);

##########################
# Building distributions #
##########################

# Build prism.jar
binary:
	@echo "Generating JAR file ($(PRISM_LIB_DIR)/prism.jar)..."
	@jar cmf $(JAVA_SRC_DIR)/manifest.txt $(PRISM_LIB_DIR)/prism.jar -C $(JAVA_CLASSES_DIR) . -C . $(PRISM_IMAGES_DIR) $(PRISM_DTDS_DIR)

# Build prism-sources.jar
source-jar:
	@echo "Generating sources JAR file ($(PRISM_LIB_DIR)/prism-sources.jar)..."
	@find $(JAVA_SRC_DIR) -type f -name '*.java' -o -name '*.form' -o -name '*.jj' | sed -e "s/^$(JAVA_SRC_DIR)./-C $(JAVA_SRC_DIR) /" > prism-sources.txt
	@jar cf $(PRISM_LIB_DIR)/prism-sources.jar @prism-sources.txt
	@rm -f prism-sources.txt

# Download a local html copy of the manual
#PRISM_MANUAL_WEBSITE := http://prismmodelchecker.localhost/manual/
PRISM_MANUAL_WEBSITE := http://www.prismmodelchecker.org/manual/
doc: clean_doc
	(cd .. && wget -r -np -k -E -nH --no-cookies --header "Cookie: setskin=offline" --restrict-file-names=windows --reject '*action=sourceblock*' $(PRISM_MANUAL_WEBSITE) $(PRISM_MANUAL_WEBSITE)/pub/skins/offline/images/)
clean_doc:
	rm -rf ../manual

# Set up version, in particular for building releases
# Unless VERSION has been passed in (as VERSION=xxx),
# extract version number from Java code using printversion
VERSION = $(shell $(PRISM_SRC_DIR)/scripts/printversion.sh $(PRISM_SRC_DIR) 2> /dev/null)

# Default branch for building source releases
BRANCH = master

# File/dir names for distribution
SRC_DIST_DIR = prism-$(VERSION)-src
SRC_DIST_FILE = $(SRC_DIST_DIR).tar.gz
BIN_DIST_DIR = prism-$(VERSION)-$(BINDISTSUFFIX)-$(BINDISTARCH)
BIN_DIST_FILE = $(BIN_DIST_DIR).tar.gz
BIN_DIST_INST = $(BIN_DIST_DIR)-installer.exe

# Build a (binary) distribution in the release directory

release: clean_all all binary release_check_version build_release clean_binary

release_check_version:
	@if [ "$(VERSION)" = "" ]; then echo "Usage: make release VERSION=4.5"; exit 1; fi

build_release:
	@if [ "$(BINDISTSUFFIX)" = "win32" -o "$(BINDISTSUFFIX)" = "win64" ]; then \
		echo Creating Windows installer in "release/$(BIN_DIST_INST)"... && \
		makensis /NOCD /DPRISM_NAME="PRISM $(VERSION)" /DPRISM_BUILD="prism-$(VERSION)" /DPRISM_BINDISTSUFFIX="$(BINDISTSUFFIX)" /DPRISM_BINDISTARCH="$(BINDISTARCH)" /DPRISM_DIR=".." $(PRISM_SRC_DIR)/nsis_script.nsi && \
		mkdir -p release && \
		mv ../"$(BIN_DIST_INST)" release; \
	else \
		echo Creating binary distribution in "release/$(BIN_DIST_FILE)"... && \
		mkdir -p release && \
		rm -rf "release/$(BIN_DIST_DIR)" && \
		rm -f "release/$(BIN_DIST_FILE)" && \
		mkdir -p "release/$(BIN_DIST_DIR)" && \
		tar cf - bin $(PRISM_LIB_DIR) install.sh etc -C .. manual prism-examples CHANGELOG.txt COPYING.txt README.md | tar xf - -C "release/$(BIN_DIST_DIR)" && \
		(cd release && tar cfz "$(BIN_DIST_FILE)" "$(BIN_DIST_DIR)") && rm -rf "release/$(BIN_DIST_DIR)"; \
	fi

release_source: release_check_version build_release_source

build_release_source:
	@echo Creating source distribution from branch "$(BRANCH)" in "release/$(SRC_DIST_FILE)"... && \
	mkdir -p release && \
	rm -rf "release/$(SRC_DIST_DIR)" && \
	rm -f "release/$(SRC_DIST_FILE)" && \
	mkdir -p "release/$(SRC_DIST_DIR)" && \
	(cd .. && git archive $(BRANCH) --format=tar) | tar xf - -C "release/$(SRC_DIST_DIR)" && \
	(cd release && tar cfz "$(SRC_DIST_FILE)" "$(SRC_DIST_DIR)") && rm -rf "release/$(SRC_DIST_DIR)"

# Build Javadoc (and put in javadoc directory)

PRISM_CLASSPATH := "$(JAVA_CLASSES_DIR)$(CLASSPATHSEP)$(PRISM_LIB_DIR)/*"

PRISM_JAVADOC_DIR := javadoc

javadoc: prism_java
	@mkdir -p $(PRISM_JAVADOC_DIR)
	@echo "Generating Javadoc..."
	javadoc -d $(PRISM_JAVADOC_DIR) \
	        -classpath $(PRISM_CLASSPATH) \
	        -quiet \
	        -Xdoclint:none \
	        @$(JAVA_SRC_FILES_LIST)

###############
# Cleaning up #
###############

# Clean main PRISM build (not CUDD or external libs)
clean: checks
	rm -rf $(JAVA_CLASSES_DIR)
	rm -f $(JAVA_SRC_FILES_LIST) $(JAVA_COMPILED_STAMP)
	rm -f $(JAVA_TESTS_SRC_FILES_LIST) $(JAVA_TESTS_COMPILED_STAMP)
	rm -rf $(PRISM_OBJ_DIR)
	rm -f $(PRISM_LIB_DIR)/$(LIBPREFIX)prism$(LIBSUFFIX)
	rm -f $(OLD_SHARED_LIB_FILES)
	rm -f $(PRISM_INCLUDE_DIR)/jni/*.h
	rm -f $(PRISM_LIB_DIR)/*jnilib
	rm -f $(PRISM_LIB_DIR)/prism.jar
	rm -f $(PRISM_LIB_DIR)/prism-sources.jar
	rm -f $(addprefix $(PRISM_BIN_DIR)/, $(notdir $(basename $(BIN_TARGETS)))) $(PRISM_BIN_DIR)/ngprism$(EXE)

celan: clean

# Clean PRISM + CUDD and external libs
clean_all: checks clean_cudd clean_ext clean

clean_cudd:
	@(cd $(CUDD_DIR) && ./clean.sh)

clean_ext:
	@(for ext in $(EXT_PACKAGES); do \
	  echo Cleaning $$ext ...; \
	  (cd ext/$$ext && \
	  $(MAKE) -s clean) \
	  || exit 1; \
	done )

# Remove just the prism.jar binary
clean_binary:
	@echo "Removing JAR file ($(PRISM_LIB_DIR)/prism.jar)..."
	@rm -f $(PRISM_LIB_DIR)/prism.jar

###############
# IDE support #
###############

# Set up for IntelliJ IDEA
intellij:
	etc/intellij/install.sh

#################################################
