#
# This file is part of Barista.
# Copyright (C) 2007-2010 Xavier Clerc.
#
# Barista is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Barista is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# PATHS

PATH_BASE=$(shell pwd)
PATH_SRC=$(PATH_BASE)/src
PATH_BIN=$(PATH_BASE)/bin
PATH_DOC=$(PATH_BASE)/ocamldoc
PATH_TESTS=$(PATH_BASE)/tests
PATH_REPORT=$(PATH_BASE)/report
PATH_OCAML_BIN=/usr/local/bin
CLASSPATH=.:/System/Library/Frameworks/JavaVM.framework/Classes/classes.jar
BISECT_FILE=$(PATH_REPORT)/out/bisect


# DEFINITIONS

OCAMLC=$(PATH_OCAML_BIN)/ocamlc
OCAMLOPT=$(PATH_OCAML_BIN)/ocamlopt
OCAMLJAVA=$(PATH_OCAML_BIN)/ocamljava
OCAMLDOC=$(PATH_OCAML_BIN)/ocamldoc
OCAMLDEP=$(PATH_OCAML_BIN)/ocamldep
OCAMLFIND=$(shell which ocamlfind || echo '')
ifeq ($(OCAMLFIND),)
	ifeq ($(COVERAGE),)
		INCLUDES_PP=-I $(PATH_SRC) -I +zip
	else
		INCLUDES_PP=-I $(PATH_SRC) -I +zip -I +bisect -pp 'camlp4o /usr/local/lib/ocaml/bisect/instrument.cma'
	endif
else
	ifeq ($(COVERAGE),)
		INCLUDES_PP=-I $(PATH_SRC) $(shell $(OCAMLFIND) query bigarray camomile str unix camlzip | xargs -L 1 echo '-I' | xargs)
	else
		INCLUDES_PP=-I $(PATH_SRC) $(shell $(OCAMLFIND) query bisect bigarray camomile str unix camlzip | xargs -L 1 echo '-I' | xargs)
	endif
endif
OCAML_COMPILE_FLAGS=-w Ael -warn-error A $(INCLUDES_PP)
OCAML_OPT_FLAGS=-inline 100
OCAML_JAVA_FLAGS=-java-package fr.x9c.barista
ifeq ($(COVERAGE),)
OCAML_LIBRARIES=bigarray camomile str unix zip
else
OCAML_LIBRARIES=bisect bigarray camomile str unix zip
endif
JAVA=java
JAVAC=javac
JAVAP=javap
JAVA_VERSION=1.6
ANT=ant
BISECT_REPORT=bisect-report

MODULES=utils inputStream outputStream consts version name descriptor signature \
  classPath constantPool accessFlag annotation byteCode instruction attribute \
  field method classFile traversal classDefinition classLoader source printer \
  disassembler lexer stackState controlFlow code assembler api
NO_INLINE=inputStream outputStream byteCode stackState assembler
INLINE_FLAG=$(if $(findstring $(shell basename $(patsubst %.ml,%,$(1))),$(NO_INLINE)),,-inline 100)
MAIN_MODULE=main
LIBRARY=baristaLibrary
LIBRARY_NAME=BaristaLibrary
EXECUTABLE=barista

OCAML_DOC_TITLE=Barista

INSTALL_DIR_BASE=$(shell $(OCAMLC) -where)
INSTALL_DIR=$(INSTALL_DIR_BASE)/barista
INSTALL_DIR_EXEC=/usr/local/bin

CMI_FILES=$(patsubst %,$(PATH_SRC)/%.cmi,$(MODULES))
CMO_FILES=$(patsubst %,$(PATH_SRC)/%.cmo,$(MODULES))
CMX_FILES=$(patsubst %,$(PATH_SRC)/%.cmx,$(MODULES))
CMJ_FILES=$(patsubst %,$(PATH_SRC)/%.cmj,$(MODULES))
CMA_FILES=$(patsubst %,%.cma,$(OCAML_LIBRARIES))
CMXA_FILES=$(patsubst %,%.cmxa,$(OCAML_LIBRARIES))
CMJA_FILES=$(patsubst %,%.cmja,$(OCAML_LIBRARIES))


# TARGETS

default:
	@echo 'available targets:'
	@echo '  all                  compiles all files (bytecode & native versions)'
	@echo '  bytecode             compiles bytecode version'
	@echo '  native               compiles native version'
	@echo '  cafesterol           compiles Cafesterol version'
	@echo '  html-doc             generates html documentation'
	@echo '  clean-all            deletes all produced files (including documentation)'
	@echo '  clean                deletes all produced files (excluding documentation)'
	@echo '  clean-doc            deletes documentation files'
	@echo '  clean-coverage       deletes coverage information'
	@echo '  install-all          copies executables and library files'
	@echo '  install-library      copies library files to OCaml directory'
	@echo '  install-executables  copies executables to binary directory'
	@echo '  install-cafesterol   copies Cafesterol files'
	@echo '  install-ocamlfind    installs both library and executables through ocamlfind'
	@echo '  tests                runs the unit and functional tests'
	@echo '  report               produces coverage information'
	@echo '  depend               generates dependency files'

all: clean-all bytecode native html-doc

bytecode: $(CMI_FILES) $(CMO_FILES) $(PATH_SRC)/$(MAIN_MODULE).cmo
	$(OCAMLC) -I $(PATH_SRC) -pack -o $(LIBRARY).cmo $(CMO_FILES)
	$(OCAMLC) -a -o $(LIBRARY).cma $(LIBRARY).cmo
	mv $(LIBRARY).cm* $(PATH_BIN)
	$(OCAMLC) $(OCAML_COMPILE_FLAGS) -o $(PATH_BIN)/$(EXECUTABLE) $(CMA_FILES) $(CMO_FILES) src/$(MAIN_MODULE).cmo

native: $(CMI_FILES) $(CMX_FILES) $(PATH_SRC)/$(MAIN_MODULE).cmx
	$(OCAMLOPT) -I $(PATH_SRC) -pack -o $(LIBRARY).cmx $(CMX_FILES)
	$(OCAMLOPT) -a -o $(LIBRARY).cmxa $(LIBRARY).cmx
	mv $(LIBRARY).cm* $(LIBRARY).a $(PATH_BIN)
	rm $(LIBRARY).o
	$(OCAMLOPT) $(OCAML_COMPILE_FLAGS) -o $(PATH_BIN)/$(EXECUTABLE).opt $(OCAML_OPT_FLAGS) $(CMXA_FILES) $(CMX_FILES) src/$(MAIN_MODULE).cmx

cafesterol: $(CMI_FILES) $(CMJ_FILES) $(PATH_SRC)/$(MAIN_MODULE).cmj
	$(OCAMLJAVA) -java-package fr.x9c.barista -I $(PATH_SRC) -pack -o $(LIBRARY).cmj $(CMJ_FILES)
	$(OCAMLJAVA) -a -o $(LIBRARY).cmja $(LIBRARY).cmj
	mv $(LIBRARY).cm* $(LIBRARY).jar $(PATH_BIN)
	rm $(LIBRARY).jo
	$(OCAMLJAVA) -standalone $(OCAML_COMPILE_FLAGS) -o $(PATH_BIN)/$(EXECUTABLE).jar $(OCAML_JAVA_FLAGS) $(CMJA_FILES) $(CMJ_FILES) src/$(MAIN_MODULE).cmj

html-doc:
	$(OCAMLDOC) -sort -html -t '$(OCAML_DOC_TITLE)' -d $(PATH_DOC) -I $(PATH_SRC) $(PATH_SRC)/*.mli

clean-all: clean clean-doc clean-coverage

clean:
	rm -f $(PATH_SRC)/*.cm*
	rm -f $(PATH_SRC)/*.o
	rm -f $(PATH_SRC)/*.jo
	rm -f $(PATH_BIN)/*.*
	rm -f $(PATH_BIN)/barista

clean-doc:
	rm -f $(PATH_DOC)/*.html
	rm -f $(PATH_DOC)/*.css

clean-coverage:
	rm -f $(PATH_REPORT)/out/*.out

install-all: install-library install-executables

install-library: 
	mkdir -p $(INSTALL_DIR)
	cp $(PATH_BIN)/*.cm* $(PATH_BIN)/*.a $(INSTALL_DIR)

install-executables:
	cp $(PATH_BIN)/$(EXECUTABLE) $(INSTALL_DIR_EXEC)
	cp $(PATH_BIN)/$(EXECUTABLE).opt $(INSTALL_DIR_EXEC)

install-cafesterol:
	cp $(PATH_BIN)/*.cmj* $(PATH_BIN)/$(LIBRARY).jar $(INSTALL_DIR)
	cp $(PATH_BIN)/$(EXECUTABLE).jar $(INSTALL_DIR_EXEC)

install-ocamlfind:
ifeq ($(OCAMLFIND),)
	@echo cannot find ocamlfind
else
	$(OCAMLFIND) query barista && $(OCAMLFIND) remove barista || echo ''
	if [ -f $(PATH_BIN)/$(EXECUTABLE).jar ]; then \
	  $(OCAMLFIND) install barista META \
	    $(PATH_BIN)/*.cm[oxi] $(PATH_BIN)/*.a \
	    $(PATH_BIN)/$(EXECUTABLE) $(PATH_BIN)/$(EXECUTABLE).opt \
	    $(PATH_BIN)/*.cmj* $(PATH_BIN)/$(LIBRARY).jar $(PATH_BIN)/$(EXECUTABLE).jar \
	else \
	  $(OCAMLFIND) install barista META \
	    $(PATH_BIN)/*.cm[oxi] $(PATH_BIN)/*.a \
	    $(PATH_BIN)/$(EXECUTABLE) $(PATH_BIN)/$(EXECUTABLE).opt
	fi
endif

tests::
	@rm -fr $(PATH_TESTS)/classes/pack
	@javac -target $(JAVA_VERSION) -d $(PATH_TESTS)/classes $(PATH_TESTS)/classes/*.java
	@rm -f $(PATH_TESTS)/java/*/*.class
	@javac -target $(JAVA_VERSION) -cp api/barista-api.jar:$(INSTALL_DIR_BASE)/ocamlrun.jar -d $(PATH_TESTS)/java $(PATH_TESTS)/java/*/*.java
	@for dir in `ls -d $(PATH_TESTS)/[0-9]*`; do \
	    (if [ ! -f $$dir ]; then \
	        echo "running tests for '`basename $$dir`' ...\c" && \
                (if [ -f $$dir/Makefile.tool ]; then \
                    echo " tool\c"; \
                    cd $$dir && $(MAKE) -f $$dir/Makefile.tool EXEC=../../bin/barista JAVA=$(JAVA) JAVAC=$(JAVAC) JAVAP=$(JAVAP) CLASSPATH=$(CLASSPATH) BISECT_FILE=$(BISECT_FILE); \
                    cd $$dir && $(MAKE) -f $$dir/Makefile.tool EXEC=../../bin/barista.opt JAVA=$(JAVA) JAVAC=$(JAVAC) JAVAP=$(JAVAP) CLASSPATH=$(CLASSPATH) BISECT_FILE=$(BISECT_FILE); \
                    test -f $(PATH_BIN)/$(EXECUTABLE).jar && (cd $$dir && $(MAKE) -f $$dir/Makefile.tool EXEC='$(JAVA) -Xmx2G -jar ../../bin/barista.jar' JAVA=$(JAVA) JAVAC=$(JAVAC) JAVAP=$(JAVAP) CLASSPATH=$(CLASSPATH) BISECT_FILE=$(BISECT_FILE)) || echo '\c'; \
                fi) && \
                (if [ -f $$dir/Makefile.ocaml ]; then \
                    echo " ocaml\c"; \
                    cd $$dir && $(MAKE) -f $$dir/Makefile.ocaml COMP=ocamlc LIB_EXT=cma PROG_EXT=bc JAVAP=$(JAVAP) RUN_JAVA='$(JAVA) -Xmx2G -cp .' COVERAGE=$(COVERAGE) BISECT_FILE=$(BISECT_FILE); \
                    cd $$dir && $(MAKE) -f $$dir/Makefile.ocaml COMP=ocamlopt LIB_EXT=cmxa PROG_EXT=opt JAVAP=$(JAVAP) RUN_JAVA='$(JAVA) -Xmx2G -cp .' COVERAGE=$(COVERAGE) BISECT_FILE=$(BISECT_FILE); \
                    test -f $(PATH_BIN)/$(EXECUTABLE).jar && (cd $$dir && $(MAKE) -f $$dir/Makefile.ocaml COMP=ocamljava LIB_EXT=cmja PROG_EXT=jar FLAGS='-standalone' RUN_PROG='$(JAVA) -Xmx2G -jar' JAVAP=$(JAVAP) RUN_JAVA='java -cp .' COVERAGE=$(COVERAGE) BISECT_FILE=$(BISECT_FILE)) || echo '\c'; \
                fi) && \
                (if [ -f $$dir/Makefile.java ]; then \
                    echo " java\c"; \
                    test -f $(PATH_BIN)/$(EXECUTABLE).jar && (cd $$dir && $(MAKE) -f $$dir/Makefile.java JAVA=$(JAVA) JAVAC=$(JAVAC) JAVAP=$(JAVAP) ANT=$(ANT) CLASSPATH=../../api/barista-api.jar:$(INSTALL_DIR_BASE)/ocamlrun.jar:../../bin/barista.jar BISECT_FILE=$(BISECT_FILE)) || echo '\c'; \
                fi) && \
	        echo ""; \
	    fi) || exit $?; \
	done
	@rm -fr $(PATH_TESTS)/classes/pack
	@rm -f $(PATH_TESTS)/java/*/*.class
	@rm -f $(PATH_TESTS)/ocaml/*.cm*
	@rm -f $(PATH_TESTS)/ocaml/*.jo
	@rm -f $(PATH_TESTS)/ocaml/*.o

report::
	rm -fr $(PATH_REPORT)/html
	$(BISECT_REPORT) $(PATH_REPORT)/out/*.out -html $(PATH_REPORT)/html


# GENERIC TARGETS

.SUFFIXES: .ml .mli .cmo .cmi .cmx .cmj

.mli.cmi:
	$(OCAMLC) $(OCAML_COMPILE_FLAGS) -c $<

.ml.cmo:
	$(OCAMLC) $(OCAML_COMPILE_FLAGS) -c $<

.ml.cmx:
	$(OCAMLOPT) $(OCAML_COMPILE_FLAGS) -for-pack $(LIBRARY_NAME) -c $(OCAML_OPT_FLAGS) $<

.ml.cmj:
	$(OCAMLJAVA) $(OCAML_COMPILE_FLAGS) $(call INLINE_FLAG,$<) -for-pack $(LIBRARY_NAME) -c $(OCAML_JAVA_FLAGS) $<


# DEPENDENCIES

depend::
	$(OCAMLDEP) -I $(PATH_SRC) $(PATH_SRC)/*.ml* > depend
	$(OCAMLDEP) -I $(PATH_SRC) $(PATH_SRC)/*.ml* | sed 's/\.cmx/\.cmj/g' > depend.cafesterol

include depend
include depend.cafesterol
