# Build DL_POLY as a static library using its native Makefile

#  Copyright (C) 2017 The authors of Py-ChemShell
#
#  This file is part of Py-ChemShell.
#
#  Py-ChemShell 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.
#
#  Py-ChemShell 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 Py-ChemShell.  If not, see
#  <http://www.gnu.org/licenses/>.

# you.lu@stfc.ac.uk
# June 2015
# updated Aug 2021 for DL_POLY 5

# YL 09/11/2021: since CMake 3.12 MPI_ROOT will be used for searching
#                see details by: cmake --help-policy CMP0074
# but seems not effective to the DLP5 CMake
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12")
    cmake_policy(SET CMP0074 NEW)
endif()

# YL 24/05/2023: this is the latest working commit in the "devel" branch; let's 
#                aim at this one instead of the default latest which could be broken
#                anytime due to update on the DL_POLY side
set(DL_POLY_DEFAULT_VERSION a8ae3dbfc085f06912f61a08a486a0fa1e401d2c)
# if DL_POLY specified by user
set(DL_POLY_NAME dl_poly-5)

# paths
file(MAKE_DIRECTORY ${CHEMSH_BUILD_DIR}/lib/dl_poly)
set(CHEMSH_DL_POLY_LIB_DIRECTORY ${CHEMSH_BUILD_DIR}/lib/dl_poly)
set(CMAKE_Fortran_MODULE_DIRECTORY ${CHEMSH_BUILD_DIR}/modules/dl_poly)
include_directories(
                    ${CHEMSH_BUILD_DIR}/modules/parallel
                    ${CHEMSH_BUILD_DIR}/modules/dl_py2f)

# without DL_POLY
if("${DL_POLY}" STREQUAL "")
    # compile libdl_poly.so
    add_library(dl_poly SHARED dl_poly.f90 dl_poly_dummies.f90 ${NOTLINKED})
    target_link_libraries(dl_poly dl_py2f parallel)
    return()
endif()

if(NOT ${DL_POLY} STREQUAL "")
    get_filename_component(DL_POLY_ABSOLUTE ${DL_POLY} ABSOLUTE)
endif()

# debug options
if(DEBUG)
    set(DL_POLY_CMAKE_OPTIONS "")
else()
    set(DL_POLY_CMAKE_OPTIONS --no-warn-unused-cli -Wno-dev)
endif()

# user specified dir or archive
if(EXISTS ${DL_POLY_ABSOLUTE})
    # if is directory
    if(IS_DIRECTORY ${DL_POLY_ABSOLUTE})
        set(DL_POLY_DIR     ${DL_POLY_ABSOLUTE})
    # if file (tarball)
    else()
        set(DL_POLY_TARBALL  ${DL_POLY_ABSOLUTE})
        set(DL_POLY_DEST_DIR ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH}/src)
        set(DL_POLY_DIR      ${DL_POLY_DEST_DIR})
        # YL 02/10/2020: FetchContent is not available until CMake 3.11 (Ubuntu 18.04 comes with CMake 3.10 only)
        if(${CMAKE_VERSION} VERSION_LESS "3.11")
            include(${CHEMSH_DIR}/chemsh/utils/cmake/fetch_content.cmake)
        else()
            include(FetchContent)
        endif()
        FetchContent_Declare(
            dl_poly_source
            URL ${DL_POLY_TARBALL}
        )
        FetchContent_GetProperties(dl_poly_source)
        if(NOT dl_poly_source_POPULATED)
            FetchContent_Populate(
                dl_poly_source
                URL          ${DL_POLY_TARBALL}
                SOURCE_DIR   ${DL_POLY_DEST_DIR}
                BINARY_DIR   ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH}/build
                SUBBUILD_DIR ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH}/subbuild
            )
        endif()
    endif()
    set(DL_POLY_SRC_DIR ${DL_POLY_DIR})
    set(DL_POLY_GIT_REPO  "")

# Git
else()
    set(DL_POLY_GIT_REPO  "${DL_POLY}")
    set(DL_POLY_DIR       ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH})
    set(DL_POLY_SRC_DIR   ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH}/src)
    if(EXISTS ${DL_POLY_SRC_DIR}/source/dl_poly.F90 AND NOT ${DL_POLY_RECOMPILE})
        set(DL_POLY_GIT_REPO "")
    endif()
endif()

# pass this variable to DL_POLY 5's CMakeCache.txt
set(DLP4_CMAKE_INSTALL_LIBDIR lib)

# YL 29/01/2022: we must hack DL_POLY 5 to enable large QM/MM calculations
configure_file(hack.py.in hack.py @ONLY)

# determine if serial or parallel build required
if(WITH_MPI)
    # note: DL_POLY 5 uses its own hacked find_package(MPI REQUIRED)
    message("${BoldWhite}DL_POLY 5${EndColour} will be built with MPI support")

    # compile flag used below
    set(DL_POLY_MPI_MACRO "-DWITH_MPI")

    # it MUST be a filename without directory (full path) specified to MPI_Fortran_COMPILER for DL_POLY, otherwise MPI_Fortran_INCLUDE_PATH goes wrong
    get_filename_component(DLP_MPI_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} NAME)
# FIXME: this is a terrible solution as DL_POLY requires MPI_Fortran_COMPILER (e.g., mpiifort or mpifort.openmpi) and CMAKE_Fortran_COMPILER (e.g., ifort or gfortran) at the same time
    if(${CHEMSH_ARCH} STREQUAL "intel")
        set(DLP_Fortran_COMPILER "ifort")
    elseif(${CHEMSH_ARCH} STREQUAL "gnu")
        set(DLP_Fortran_COMPILER "gfortran")
    # YL 18/11/2021: added for ARCHER2, otherwise DLP5 cannot find MPI (as always!)
    elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
        set(DLP_Fortran_COMPILER ${CMAKE_Fortran_COMPILER})
        string(REPLACE ";" " " DLP_MPI_LIBRARIES "${MPI_LIBRARIES}")
        # YL 22/12/2021: removed the flag for the ARCHER2 full system with CMake 3.21 otherwise DL_POLY tries to link with -l"-L/xxxx -lmpi" strangely
        if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21")
        else()
            set(DLP_MPI_Fortran_LINK_FLAGS -DMPI_Fortran_LINK_FLAGS:STRING=-L${MPI_LIB_PATH} ${DLP_MPI_LIBRARIES})
        endif()
    endif()

#    # on ARCHER, DL_POLY 5's homebrewed FindMPI function does not identify MPI, but the following method cannot fix it (have to hack DL_POLY CMakeLists.txt)
#    if(${PLATFORM} STREQUAL "archer")
#        #set(ARCHER_MPI_FLAGS "-DMPI_Fortran_INCLUDE_PATH:STRING=${MPI_INCLUDE_PATH} -DMPI_Fortran_LIBRARIES:STRING=${MPI_LIBRARIES}")
#        # told by Alin but does not work!
#        set(DLP_MPI_Fortran_COMPILER "${DLP_MPI_Fortran_COMPILER} -dynamic")
#    endif()

    # in ChemShell, MPI_Fortran_LIBRARIES is not set via MPI_LIBRARIES (--mpi_libraries in setup cmd line) for intel arch (see chemsh/CMakeLists.txt), however on some systems using MPICH (e.g. ARCHER) this kind of variables are required
    if("${MPI_Fortran_LIBRARIES}" STREQUAL "")
        set(DLP4_MPI_Fortran_LIBRARIES_ARGS    "-DMPI_Fortran_LIBRARIES=${MPI_LIBRARIES}")
        set(DLP4_MPI_Fortran_INCLUDE_PATH_ARGS "-DMPI_Fortran_INCLUDE_PATH=${MPI_INCLUDE_PATH}")
    endif()

    # YL 09/11/2021: -DNVIDIA to prevent DLP5 using compiler_version() which is F2008
    # YL 21/11/2021: removed -DNVIDIA which causes numerical error with GNU>=10.2
    set(DL_POLY_Fortran_FLAGS "-fPIC ${CMAKE_Fortran_FLAGS}")
    # the directory ${DL_POLY}:MUST be placed in the end, otherwise CMake goes crazy
    set(DL_POLY_CONFIGURE_COMMAND ${CMAKE_COMMAND} -DWITH_MPI:BOOL=ON
                                                   -DINTERFACED:BOOL=ON
                                                   -DCMAKE_Fortran_COMPILER:STRING=${DLP_Fortran_COMPILER}
                                                   -DCMAKE_Fortran_COMPILER_ID:STRING=${CMAKE_Fortran_COMPILER_ID}
                                                   -DCMAKE_Fortran_COMPILER_VERSION:STRING=${CMAKE_Fortran_COMPILER_VERSION}
                                                   -DMPI_Fortran_COMPILER:STRING=${MPI_Fortran_COMPILER}
                                                   ${DLP_MPI_Fortran_LINK_FLAGS}
                                                   -DCMAKE_Fortran_FLAGS:STRING=${DL_POLY_Fortran_FLAGS}
                                                   -DMPI_INCLUDE_PATH:PATH=${MPI_INCLUDE_PATH}
                                                   -DMPI_LIBRARY:PATH=${MPI_LIBRARY}
                                                   ${DLP4_MPI_Fortran_LIBRARIES_ARGS}
                                                   ${DLP4_MPI_Fortran_INCLUDE_PATH_ARGS}
                                                   -DHOST:STRING=${HOSTNAME}
                                                   -DCMAKE_INSTALL_LIBDIR:PATH=${DLP4_CMAKE_INSTALL_LIBDIR}
                                                   ${DL_POLY_CMAKE_OPTIONS}
                                                   ${DL_POLY_SRC_DIR})
else()

    message("${BoldWhite}DL_POLY 5${EndColour} library will be built without MPI support")

    # YL 09/11/2021: -DNVIDIA to prevent DLP5 using compiler_version() which is F2008
    # YL 21/11/2021: removed -DNVIDIA which causes numerical error with GNU>=10.2
    set(DL_POLY_MPI_MACRO "-UWITH_MPI")
    set(DL_POLY_Fortran_FLAGS "-fPIC ${CMAKE_Fortran_FLAGS}")

    set(DL_POLY_CONFIGURE_COMMAND ${CMAKE_COMMAND} -DWITH_MPI:BOOL=OFF
                                                   -DINTERFACED:BOOL=ON
                                                   -DCMAKE_Fortran_COMPILER:STRING=${CMAKE_Fortran_COMPILER}
                                                   -DCMAKE_Fortran_COMPILER_ID:STRING=${CMAKE_Fortran_COMPILER_ID}
                                                   -DCMAKE_Fortran_COMPILER_VERSION:STRING=${CMAKE_Fortran_COMPILER_VERSION}
                                                   -DCMAKE_Fortran_FLAGS:STRING=${DL_POLY_Fortran_FLAGS}
                                                   -DHOST:STRING=${HOSTNAME}
                                                   -DCMAKE_INSTALL_LIBDIR:PATH=${DLP4_CMAKE_INSTALL_LIBDIR}
                                                   ${DL_POLY_CMAKE_OPTIONS}
                                                   ${DL_POLY_SRC_DIR})
endif()
    
# switch: recompile
set(DL_POLY_CLEAN_COMMAND make clean)
set(DL_POLY_MAKE_COMMAND make -j8)

# YL 16/02/2019: DLP4_CMAKE_INSTALL_LIBDIR has to be hardcoded otherwise it can be either "lib" or "lib64" in DL_POLY 5 determined by CMake
set(DL_POLY_LIB_DIRECTORY ${DL_POLY_SRC_DIR}/${DLP4_CMAKE_INSTALL_LIBDIR})
set(DL_POLY_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory ${DL_POLY_LIB_DIRECTORY} ${CHEMSH_DL_POLY_LIB_DIRECTORY})

if(${DL_POLY_RECOMPILE})
    set(DL_POLY_RECOMPILE 1)
    file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)
    # do not overwrite the downloaded source code by a new clone (default: OFF)
    if(RECOMPILE_WITHOUT_DOWNLOAD)
        set(DL_POLY_GIT_REPO "")
    endif()
else()
    if(EXISTS ${CHEMSH_DL_POLY_LIB_DIRECTORY}/libdlpoly.a)
        set(DL_POLY_RECOMPILE 0)
        set(DL_POLY_CLEAN_COMMAND   ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --blue --bold "Found all required static libraries. Skipped cleaning step")
        set(DL_POLY_MAKE_COMMAND    ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --blue --bold "Skipped recompile step")
        set(DL_POLY_INSTALL_COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --blue --bold "Skipped installation step")
        message("${BoldRed}DL_POLY_RECOMPILE${EndColour} set ${BoldWhite}OFF${EndColour}: will not recompile DL_POLY")
    else()
        set(DL_POLY_RECOMPILE 1)
        file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)
        message("${Green}DL_POLY_RECOMPILE${EndColour} set ${BoldWhite}OFF${EndColour} but necessary static libraries missing under ${Yellow}${CHEMSH_DL_POLY_LIB_DIRECTORY}${EndColour}: DL_POLY source code will still be recompiled")
    endif()
endif()

include(ExternalProject)
# compile DL_POLY as an external project using its original Makefile
ExternalProject_Add(
    ${DL_POLY_NAME}
    GIT_REPOSITORY      ${DL_POLY_GIT_REPO}
    GIT_SHALLOW         0
    GIT_PROGRESS        ${DEBUG}
#    GIT_PROGRESS        ${DEBUG}
    GIT_CONFIG          advice.detachedHead=false
    
    GIT_TAG             ${DL_POLY_DEFAULT_VERSION}
    UPDATE_DISCONNECTED 1
    UPDATE_COMMAND      pwd
    PREFIX              ${CHEMSH_ARCH}
    STAMP_DIR           ${CHEMSH_ARCH}/stamp
    TMP_DIR             ${CHEMSH_ARCH}/tmp
    BUILD_ALWAYS        ${DL_POLY_RECOMPILE}
    SOURCE_DIR          ${DL_POLY_SRC_DIR}
    BINARY_DIR          ${DL_POLY_DIR}/build
    CONFIGURE_COMMAND   ""
    BUILD_COMMAND       ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --magenta --bold "Architecture for which '${DL_POLY_NAME}' is built: '${CHEMSH_ARCH}'"
    INSTALL_COMMAND     ""
)

ExternalProject_Add_Step(
    ${DL_POLY_NAME} DL_POLY_HACK
    COMMENT           "Hacking ${DL_POLY_SRC_DIR}/source/ffield.F90..."
    ALWAYS            1
    DEPENDEES         configure
    # YL 17/08/2021: CMake doesn't have a standard patchting method (`cmake -E patch ...` won't work) and file/string processing actions at the configure stage
    COMMAND           ${PYTHON_EXECUTABLE} hack.py
    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)

ExternalProject_Add_Step(
    ${DL_POLY_NAME} DL_POLY_CONFIGURE
    COMMENT           "Cleaning up previous binaries for '${DL_POLY_NAME}'..."
    ALWAYS            ${DL_POLY_RECOMPILE}
    DEPENDEES         DL_POLY_HACK
    COMMAND           ${DL_POLY_CONFIGURE_COMMAND}
    WORKING_DIRECTORY ${DL_POLY_SRC_DIR}
)

ExternalProject_Add_Step(
    ${DL_POLY_NAME} DL_POLY_BUILD
    COMMENT           "Building static library for '${DL_POLY_NAME}'..."
    ALWAYS            ${DL_POLY_RECOMPILE}
    # always preceded by DL_POLY_CONFIGURE
    DEPENDEES         DL_POLY_CONFIGURE
    COMMAND           ${DL_POLY_CLEAN_COMMAND} COMMAND ${DL_POLY_MAKE_COMMAND}
    WORKING_DIRECTORY ${DL_POLY_SRC_DIR}
)

ExternalProject_Add_Step(
    ${DL_POLY_NAME} DL_POLY_INSTALL
    COMMENT           "Installing static library to ${CHEMSH_DL_POLY_LIB_DIRECTORY}"
    ALWAYS            ${DL_POLY_RECOMPILE}
    # always preceded by DL_POLY_BUILD
    DEPENDEES         DL_POLY_BUILD
    COMMAND           ${DL_POLY_INSTALL_COMMAND}
    WORKING_DIRECTORY ${DL_POLY_SRC_DIR}
)

# YL 08/10/2021: we touch a dummy file because ExternalProject_Add() is a compiletime command, however
#                add_library(dl_poly ...) needs dl_poly_interface.F90 at the configure stage
if(NOT EXISTS ${DL_POLY_SRC_DIR}/source/dl_poly_interface.F90)
    file(MAKE_DIRECTORY ${DL_POLY_SRC_DIR}/source)
    execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${DL_POLY_SRC_DIR}/source/dl_poly_interface.F90)
endif()

# compile libdl_poly.so (containing only Py-ChemShell interface files)
# YL 05/10/2021: the main routine file will be released with DL_POLY 5 and renamed to 
add_library(dl_poly SHARED dl_poly.f90 ${DL_POLY_SRC_DIR}/source/dl_poly_interface.F90 ${LINKED})

target_include_directories(dl_poly BEFORE PRIVATE ${DL_POLY_SRC_DIR}
                                   PRIVATE ${DL_POLY_SRC_DIR}/source)

# add_dependencies to make sure target_link_libraries is performed only after ExternalProject_Add_Step is finished
add_dependencies(dl_poly ${DL_POLY_NAME} dl_py2f parallel)

if(${CHEMSH_ARCH} STREQUAL "gnu")
    set_target_properties(dl_poly PROPERTIES COMPILE_FLAGS "${DL_POLY_MPI_MACRO} -cpp")
elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
    set_target_properties(dl_poly PROPERTIES COMPILE_FLAGS "${DL_POLY_MPI_MACRO} -cpp")
elseif(${CHEMSH_ARCH} STREQUAL "intel")
    set_target_properties(dl_poly PROPERTIES COMPILE_FLAGS "${DL_POLY_MPI_MACRO} -fpp")
endif()

set_target_properties(dl_poly PROPERTIES LINK_FLAGS "-rdynamic -Wl,--export-dynamic                            \
                                                     -Wl,--whole-archive                                       \
                                                     ${CHEMSH_DL_POLY_LIB_DIRECTORY}/libdlpoly.a               \
                                                     -Wl,--no-whole-archive                                    \
                                                     -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/export.map \
                                                     ")
#-Wl,--allow-multiple-definition

# link against ChemShell libs
target_link_libraries(dl_poly dl_py2f parallel)

