#!/usr/bin/make -f
# =====================================================================
#		     GNU Makefile for MetalWalls
# =====================================================================
#
# Targets
# -------
#
# all
# Compile the metalwalls executable
#
# tests
# Compile the metalwalls executable and run tests
#
# documentation
# Generate the documentation using Doxygen
#
# TAGS
# Generate emacs TAGS file
#
# clean
# Remove object files and mod files
#
SHELL = /bin/bash
# Clears out the suffix list
.SUFFIXES:
.SUFFIXES: .o .f90 .F90
Release ?= 20.05

# Default target
.PHONY: all
all:

# ======================================================================
# System configuration
# ======================================================================
include config.mk

# Include Plumed.inc if present otherwise does nothing (the minus does the trick)
-include Plumed.inc

$(info Fortran compiler: $(F90))
$(info Fortran compilation flags: $(F90FLAGS))
$(info Fortran preprocessing flags: $(FPPFLAGS))
$(info Linker flags: $(LDFLAGS))
$(info Path to pFUnit installation directory: $(PFUNIT))


# Variables below can be set in computers/config.mk if needed
AR ?= ar
ARFLAGS ?= rv
RM ?= rm -f
MKDIR_P ?= mkdir -p
SED ?= sed
MV ?= mv
CP ?= cp


# ======================================================================
# Beginning of Makefile definition
# ======================================================================

# Define directory variables
root_dir := .
src_dir := $(root_dir)/src
build_dir := $(root_dir)/build
tests_dir := $(root_dir)/tests
python_dir := $(root_dir)/build/python

mw := mw

# Object files
objs_dir = $(addprefix $(build_dir)/,$(srcs_dir))
src_objs = $(patsubst %.f90,$(build_dir)/%.o,$(srcs_f90))
src_objs += $(patsubst %.F90,$(build_dir)/%.o,$(srcs_F90))
pf_objs = $(patsubst %.pf,$(build_dir)/%_pf.o,$(srcs_pf))
objs = $(src_objs) $(pf_objs)

# mod files
mod_dir := $(build_dir)/include
mod_prefix := mw_
mods = $(patsubst %.f90,$(mod_dir)/$(mod_prefix)%.mod, $(notdir $(srcs_f90)))
mods += $(patsubst %.F90,$(mod_dir)/$(mod_prefix)%.mod, $(notdir $(srcs_F90)))
mods += $(patsubst %.pf,$(mod_dir)/$(mod_prefix)%.mod, $(notdir $(srcs_pf)))
F90FLAGS += $(J)$(mod_dir)

# preprocessed files
prep_dir = $(build_dir)/preprocess
prepsf = $(patsubst %.f90,$(prep_dir)/%.f90, $(notdir $(srcs_f90)))
prepsF += $(patsubst %.F90,$(prep_dir)/%.F90, $(notdir $(srcs_F90)))
preps = $(prepsf) $(prepsF)

# Include srcs definitions from subdirectories
srcs_dir :=
srcs_f90 :=
srcs_F90 :=
srcs_pf  :=
srcs_inc :=
srcs_inc_to_delete :=
srcs = $(srcs_f90) $(srcs_F90) $(srcs_pf) $(srcs_inc)

subdirs := src tests/pFUnit
include $(patsubst %, %/module.mk, $(subdirs))


.PHONY: prep
prep: $(src_dir)/version_compilation_flags.inc $(src_dir)/version_module_list.inc $(src_dir)/version_git_info.inc $(preps)
	echo "preprocessed $^ "

# Build mw program
$(mw): $(src_objs)
	$(F90) $(F90FLAGS) -o $@  $^ $(LDFLAGS) $(PLUMED_LOAD)

all: $(mw)


# Clean everything
.PHONY: clean
clean:
	$(RM) $(objs) $(mods) $(src_dir)/*_pf.* $(srcs_inc_to_delete) ./dependencies.mk $(mw) mw_tests
	$(RM) $(python_dir)/f90wrap_* $(python_dir)/metalwalls.py* $(python_dir)/*.so
	$(RM) $(prep_dir)/*f90 $(prep_dir)/*F90 $(prep_dir)/*inc
	$(RM) f90wrap_* metalwalls.py* *.so


# Generate emacs TAGs table
TAGS: $(srcs_f90) $(srcs_F90)
	etags $^

# Tests
testsData_dir := $(tests_dir)/pFUnit
testSuites_dir := $(tests_dir)/pFUnit
testSuites := $(testSuites_dir)/testSuites.inc
mw_tests: $(testSuites) $(pf_objs)
	$(F90) $(PLUMED_LOAD) -o $@ -I$(PFUNIT)/mod -I$(PFUNIT)/include -I$(testSuites_dir) \
      $(PFUNIT)/include/driver.F90 $(filter-out %main.o,$(src_objs)) $(pf_objs) \
      -L$(PFUNIT)/lib -lpfunit $(F90FLAGS) $(LDFLAGS) $(PLUMED_LOAD)

# Perform self-tests
.PHONY: check
check: mw_tests $(mw)
	cd $(testsData_dir) && mpirun -n 1 $(abspath $(root_dir))/mw_tests

# Perform global tests
.PHONY: test
Run-tests ?=
test: $(mw)
	cd $(tests_dir) && python regression_tests.py $(Run-tests)

# Build python-interface library
.PHONY: python
src_python = $(filter-out $(prep_dir)/electrode.f90 $(prep_dir)/ion.f90 $(prep_dir)/molecule.f90 $(prep_dir)/localwork.f90 $(prep_dir)/kinds.F90 $(prep_dir)/constants.F90 $(prep_dir)/thomas_fermi.f90, $(preps))

python: $(mw) $(python_dir) $(preps)
	echo "Src python $(src_python)"

	$(RM) $(root_dir)/build/src/main.o

	# Generate f90wrap wrappers that handle derived types
	$(F90WRAP) -m metalwalls -k $(root_dir)/.f2py_f2cmap $(src_python)

	@scripts/modify_python_f90wrap.sh

	$(F2PY) -c --f90exec=$(F90) --fcompiler=$(FCOMPILER) -m _metalwalls -I$(mod_dir) $(build_dir)/src/*.o --f90flags="$(F90FLAGS) $(FPPFLAGS)" $(LDFLAGS) $(PLUMED_LOAD) $(PLUMED_DEPENDENCIES) f90wrap_*.f90
	$(MV) f90wrap_*.f90 metalwalls.py _metalwalls*.so $(python_dir)
	$(CP) src/*.py $(python_dir)

# Rule to create directory if they don't exist
$(objs): | $(objs_dir) $(mod_dir)
$(mods): | $(mod_dir)
$(preps): | $(prep_dir)
$(objs_dir) $(mod_dir) $(prep_dir) $(python_dir) :
	$(MKDIR_P) $@


# General rules for building prog.o from prog.f90 or prog.F90.
#
# Here we indicate to Make that the compilation command produces two
# files. The object and mod files both depends on the source file. Now
# dependency lists for objects files need only to specify .mod files
# for used modules and .inc files for includes.
#
# Use FORCE as a pre-requisite for targets which always needs to be created
# If the target does not create a file (clean for example) use the .PHONY mechanism
FORCE:

# Construct an F90 file from a preprocessor input file
$(build_dir)/%_pf.F90: %.pf
	$(PFUNIT)/bin/pFUnitParser.py $< $@

$(build_dir)/%.o: %.f90
	$(F90) $(F90FLAGS) -o $@  -c $<

# Same rule as above but for .F90 files which requires preprocessing
# Note the addition of the FPPFLAGS variables on the compilation commang line
$(build_dir)/%.o: %.F90
	$(F90) $(FPPFLAGS) $(F90FLAGS) -o $@ -c $<

# Same rule as above but for _pf.F90 files
$(build_dir)/%_pf.o: $(build_dir)/%_pf.F90
	$(F90) $(FPPFLAGS) $(F90FLAGS) -I$(PFUNIT)/mod -I$(PFUNIT)/include -o $@ -c $<


# Build preprocessed files
#$(prep_dir)/%.f90: $(src_dir)/%.f90
#	$(F90) -E $(FPPFLAGS) $(F90FLAGS) -o $@ -c $^

#$(prep_dir)/%.F90: $(src_dir)/%.F90
#	$(F90) -E $(FPPFLAGS) $(F90FLAGS) -o $@ -c $^

# Build preprocessed files Intel style
$(prep_dir)/%.f90: $(src_dir)/%.f90
	$(F90) -E $(FPPFLAGS) $(F90FLAGS) -c $^ > $@

$(prep_dir)/%.F90: $(src_dir)/%.F90
	$(F90) -E $(FPPFLAGS) $(F90FLAGS) -c $^ > $@


# Generate 2DPBC version from PBC version
%_2DPBC.inc: %_PBC.inc
	$(SED) 's/@PBC@/2DPBC/g' $< > $@

# Generate 3DPBC version from PBC version
%_3DPBC.inc: %_PBC.inc
	$(SED) 's/@PBC@/3DPBC/g' $< > $@

# Keep inc files that are specific and cannot be generated
#$(gen_src_dir)/minimum_image_displacement_3DPBC.inc: src/minimum_image_displacement_3DPBC.inc
#	cp $< $@



# Generate a file with compilation flag informations
src/version_compilation_flags.inc: FORCE
	@echo Generating $@
	@python scripts/gen_compilation_flags.py $@ "$(shell hostname) " "$(F90) " "$(F90FLAGS) " "$(FPPFLAGS) " "$(LDFLAGS) "

# Generate a file with loaded module informations
src/version_module_list.inc: FORCE
	@echo Generating $@
	@scripts/gen_module_list.sh $@

# Generate a file with git status information
gen_release ?= no
src/version_git_info.inc: FORCE
	@echo Generating $@
	@scripts/gen_git_info.sh $@ $(Release) $(gen_release)

dependencies.mk:
	python scripts/pymakedepf90.py $(srcs)
include dependencies.mk
