#
# Copyright (C) 2003 Damien Pous 
#  
#  This file is part of Mocambients.
#  
#  Mocambients is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  Mocambients 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 General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with Mocambients; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Generic Makefile for use with ocaml, 
#
#  partially derived from 
#    OCamlMakefile Copyright (C) 1999-2003 Markus Mottl
#  <http://www.oefai.at/~markus/ocaml_sources>
#  
# 
#
# Here is the list of the meaningful variables
#
# SOURCES : 	list of source files to compile
#           the following extensions are supported: 
#           - .mli   : ocaml interface
#           - .ml    : ocaml implementation
#           - .mll   : ocamllex lexer
#           - .mly   : ocamlyacc parser
#           - .rep   : cameleon report
#           - .zog   : cameleon zoggy
#           - .glade : glade project
#           - .pack  : pack file, ie a list source files residing in a directory
#                        the resulting objects will be packed into a single one
#
# RESULT : 	name of the executable/library to build
#
# INCDIRS : 	external directories for finding .cmi files
#
# LIBS : 	libraries to link with (don't put the suffix)
#
# TRASH : 	special files to remove when doing "make clean"
#
# OMAKEFILE : 	location of the OMakefile
#
# BYTECODE / NATIVE : 
#		set one of these variable to use the corresponding compilation mode
#
# QUIET : 	unset it to see every command achieved by make
#
# PROFILING : 	set it in order to activate profiling
#
# DEBUG : 	set it in order to activate debugging
#
# ANNOTATE : 	set it in order to create .annot files, for use with emacs/caml-types
#
# CUSTOM : 	set it in order to compile bytecode in custom mode
#
# USE_CAMLP4 : 	set it in order to use camlp4 with files beginning with (*pp ... *)
# 
# OCAMLC, OCAMLOPT, CAMLP4, OCAMLCP, OCAMLMKTOP, OCAMLDEP, OCAMLLEX, OCAMLYACC, 
# CAMELEON_REPORT, CAMELEON_ZOGGY, OCAML_GLADECC :
#		override these variables to use your own compilers, or specify 
#		special options



######################################################################
# non exported variables

ifndef SOURCES
  SOURCES := foo.ml
endif

ifndef RESULT
  RESULT := foo
endif

ifndef OCAMLC
  OCAMLC := ocamlc
endif

ifndef OCAMLOPT
  OCAMLOPT := ocamlopt
endif

ifndef OCAMLCP
  OCAMLCP := ocamlcp -p a
endif

ifndef OCAMLMKTOP
  OCAMLMKTOP := ocamlmktop
endif

ifdef PROFILING
  OCAMLC := $(OCAMLCP)
  OCAMLOPT += -p
endif

ifdef DEBUG
  OCAMLC += -g
  OCAMLOPT += -g
endif

ifdef ANNOTATE
  OCAMLC += -dtypes
  OCAMLOPT += -dtypes
endif

ifdef CUSTOM
  CUSTOM := -custom
endif


######################################################################
# exported variables (mainly for dependencies)

ifdef BYTECODE
  ifdef NATIVE
    OBC := $(origin BYTECODE)
    ONC := $(origin NATIVE)
    ifeq "$(OBC)" "$(ONC)"
      ARF := $(error: please choose bytecode OR native compilation)
    else
      ifeq "$(ONC)" "command line"
        NATIVE := yes
      else
	NATIVE := 
      endif
    endif
  endif
endif

ifndef OCAMLDEP
  OCAMLDEP := ocamldep
endif
ifdef NATIVE
  OCAMLDEP += -native
  DEP_EXTN = .xdepend
  CM_EXTN  = cmx
  LIB_EXTN = cmxa
else
  DEP_EXTN = .depend
  CM_EXTN  = cmo
  LIB_EXTN = cma
endif
export OCAMLDEP DEP_EXTN CM_EXTN NATIVE

ifndef OCAMLLEX
  OCAMLLEX := ocamllex
endif
export OCAMLLEX

ifndef OCAMLYACC
  OCAMLYACC := ocamlyacc
endif
export OCAMLYACC

ifndef CAMELEON_REPORT
  CAMELEON_REPORT := report
endif
export CAMELEON_REPORT

ifndef CAMELEON_ZOGGY
  CAMELEON_ZOGGY := camlp4o pa_zog.cma pr_o.cmo
endif
export CAMELEON_ZOGGY

ifndef OCAML_GLADECC
  OCAML_GLADECC := lablgladecc2
endif
export OCAML_GLADECC

ifndef CAMLP4
  CAMLP4 := camlp4
endif
export CAMLP4

ifdef USE_CAMLP4
  PP = $(shell sed -n -e '/^\#/d' -e 's/(\*pp \([^*]*\) \*)/-pp "\1"/p;q' $<)
else
  PP = 
endif
export USE_CAMLP4

ifndef OMAKEFILE
  OMAKEFILE := OMakefile
endif
export OMAKEFILE

QUIET = @
export QUIET


######################################################################
# source files / objects definitions

EXTNS        := %.mli %.ml %.mll %.mly %.rep %.zog %.glade %.pack
UNKNOWN      := $(filter-out $(EXTNS),$(SOURCES))
ERR          := $(if $(UNKNOWN),$(error Don\'t know how to compile $(UNKNOWN)),)

BDIR         := $(notdir $(PWD))

SOURCE_DIRS  += $(filter-out ./, $(sort $(dir $(SOURCES))))
DEP_INCLUDES := $(patsubst %,-I %,$(SOURCE_DIRS))
INCLUDES     := $(patsubst %,-I %,$(INCDIRS)) $(DEP_INCLUDES)
export SOURCE_DIRS

SOURCES_ML   := $(filter %.ml, $(SOURCES))

SOURCES_MLI  := $(filter %.mli, $(SOURCES))

SOURCES_MLL  := $(filter %.mll, $(SOURCES))
AUTO_MLL     := $(SOURCES_MLL:.mll=.ml)

SOURCES_MLY  := $(filter %.mly, $(SOURCES))
AUTO_MLY     := $(SOURCES_MLY:.mly=.mli) $(SOURCES_MLY:.mly=.ml)

FILTERED_REP := $(filter %.rep, $(FILTERED))
AUTO_REP     := $(FILTERED_REP:.rep=.ml)

FILTERED_ZOG := $(filter %.zog, $(FILTERED))
AUTO_ZOG     := $(FILTERED_ZOG:.zog=.ml)

SOURCES_GLADE:= $(filter %.glade, $(SOURCES))
AUTO_GLADE   := $(SOURCES_GLADE:.glade=.ml)

SOURCES_PACK := $(filter %.pack, $(SOURCES))
AUTO_PACK    := $(SOURCES_PACK:.pack=.mli)
PACKAGE_DIRS := $(AUTO_PACK:.ml=/)
TRASH        += $(AUTO_PACK)

PRE_TARGETS  += $(AUTO_MLL) $(AUTO_MLY) $(AUTO_REP) $(AUTO_ZOG) $(AUTO_GLADE)

OBJECTS      := $(addsuffix .cmo,$(basename $(filter-out %.mli,$(SOURCES))))
OBJECTS_OPT  := $(OBJECTS:.cmo=.cmx)
OBJECTS_DEP  := $(foreach f, $(SOURCES_ML) $(SOURCES_MLI) $(SOURCES_PACK) $(AUTO_MLL) $(AUTO_MLY) $(AUTO_REP) $(AUTO_ZOG) $(AUTO_GLADE),$(DEP_EXTN).d/$(f)$(DEP_EXTN))
TRASH        += $(OBJECTS) $(OBJECTS_OPT) $(OBJECTS:.cmo=.o) $(OBJECTS:.cmo=.cmi) $(OBJECTS:.cmo=.annot)


getmodules   = $(addprefix $(3),$(addsuffix .$(2),$(basename $(filter-out %.mli,$(shell cat $(1))))))
getdirs_     = $(if $(findstring $(1),./),,$(1) $(call getdirs,$(1:/=)))
getdirs      = $(call getdirs_,$(dir $(1)))
getsources   = $(addprefix $(1:.pack=)/,$(shell cat $(1)))
ALL_INCLUDES = $(INCLUDES) $(patsubst %,-I %,$(call getdirs,$<))


ifdef NATIVE
  OCAMLMODE   = $(OCAMLOPT)
  OBJECTSMODE =	$(OBJECTS_OPT)
  CUSTOM      =
else
  OCAMLMODE   = $(OCAMLC)
  OBJECTSMODE =	$(OBJECTS)
endif
LIBSMODE    = $(addsuffix .$(LIB_EXTN),$(LIBS))


.FORCE: 

######################################################################
# Rules

%.ml :		%.mll
	$(OCAMLLEX) $<

%.ml %.mli :	%.mly
	$(OCAMLYACC) $<

%.ml :		%.rep
	$(CAMELEON_REPORT) -gen $<

%.ml :		%.zog
	$(CAMELEON_ZOGGY) -impl $< > $@

%.ml :		%.glade
	$(OCAML_GLADECC) $< > $@

%.cmi : %.pack
	$(QUIET)true

%.$(CM_EXTN) : %.pack
	$(OCAMLMODE) -pack -o $@ $(ALL_INCLUDES) -I $(<:.pack=) $(call getmodules,$<,$(CM_EXTN))

%.cmi :		%.mli
	$(OCAMLMODE) $(PP) -c $(ALL_INCLUDES) $<

%.$(CM_EXTN) :	%.ml
	$(OCAMLMODE) $(PP) -c $(ALL_INCLUDES) $<


######################################################################
# Dependencies 

.PHONY: .autopacks
.autopacks:
	$(QUIET)for i in $(AUTO_PACK); do echo "(* dummy auto-generated file, don't edit *)" > $$i; done

ifndef DEPEND
  DEPEND := $(DEP_EXTN)
endif

$(DEP_EXTN).d/%.pack$(DEP_EXTN) : %.pack %/
	@echo make[_]: Entering directory $(basename $<)
	$(QUIET)$(MAKE) --no-print-directory -f $(OMAKEFILE) dep \
		DEPEND=$@ SOURCES="$(call getsources,$<)"
	@echo make[_]: Leaving directory $(basename $<)

$(DEP_EXTN).d/%$(DEP_EXTN) : %
	$(QUIET)mkdir -p $(dir $@)
	$(OCAMLDEP) $(DEP_INCLUDES) $< > $@

.PHONY : dep
dep : $(DEPEND)

$(DEPEND) : .autopacks $(PRE_TARGETS) $(OBJECTS_DEP)
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)cat $(OBJECTS_DEP) > $@
ifdef SOURCES_PACK
	$(QUIET)echo -e $(foreach p,$(SOURCES_PACK),\
\\n$(p:.pack=.$(CM_EXTN)): $(call getmodules,$(p),$(CM_EXTN),$(p:.pack=/))\
\\n$(p:.pack=.cmi): $(p:.pack=.$(CM_EXTN))) >> $@
	$(QUIET)rm -f $(AUTO_PACK)
endif


######################################################################
# Housekeeping

%.clean : %.pack .FORCE
	@echo make[_]: Entering directory $(basename $<)
	$(QUIET)$(MAKE) --no-print-directory -f $(OMAKEFILE) clean \
		SOURCES="$(call getsources,$<)" 
	@echo make[_]: Leaving directory $(basename $<)

clean : $(SOURCES_PACK:.pack=.clean) cleanup .FORCE
	rm -f $(RESULT) $(RESULT).cma $(RESULT).cmxa $(RESULT).top

cleanup : .FORCE
	rm -rf .depend .xdepend .depend.d/ .xdepend.d/
	rm -f $(PRE_TARGETS) $(TRASH)

nobackup : 
	find . -name '*~' -o -name '*.bak' | xargs rm -f

bz2 : clean nobackup
	tar cjf ../$(BDIR)_$(shell date +%F).tar.bz2 -C .. $(BDIR)

gz : clean nobackup
	tar czf ../$(BDIR)_$(shell date +%F).tar.gz -C .. $(BDIR)


######################################################################
# Final targets

all: $(RESULT)
$(RESULT) : $(OBJECTSMODE)
	$(OCAMLMODE) $(CUSTOM) -o $@ $(INCLUDES) $(LIBSMODE) $^

nl no-link: $(OBJECTSMODE) 

lib: $(RESULT).$(LIB_EXTN)
$(RESULT).$(LIB_EXTN): $(OBJECTSMODE)
	$(OCAMLMODE) -a -o $@ $(INCLUDES) $(LIBSMODE) $^

top: $(RESULT).top
ifndef NATIVE
$(RESULT).top : $(OBJECTSMODE)
	$(OCAMLMKTOP) -o $@ $(INCLUDES) $(LIBSMODE) $^
else
$(RESULT).top : 
	$(error Cannot create a toplevel in native mode)
endif



-include $(DEP_EXTN)
