# Build GAMESS-UK 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
# Jan 2017

# we only support Intel
# GAMESS-UK can only recognise "ftn" (on ARCHER) or "intel15.0"
get_filename_component(GAMESS-UK_COMPILER ${CMAKE_Fortran_COMPILER} NAME)
if(GAMESS-UK_COMPILER STREQUAL "mpiifort" OR GAMESS-UK_COMPILER STREQUAL "ifort")
    set(GAMESS-UK_COMPILER_ARCH "intel15.0")
else()
    set(GAMESS-UK_COMPILER_ARCH "${GAMESS-UK_COMPILER}")
endif()

# paths
file(MAKE_DIRECTORY ${CHEMSH_BUILD_DIR}/lib/gamess_uk)
set(CHEMSH_GAMESS-UK_LIB_DIR ${CHEMSH_BUILD_DIR}/lib/gamess_uk)
set(CMAKE_Fortran_MODULE_DIRECTORY ${CHEMSH_BUILD_DIR}/modules/gamess_uk)
include_directories(${CHEMSH_BUILD_DIR}/modules
                    ${CHEMSH_BUILD_DIR}/modules/parallel
                    ${CHEMSH_BUILD_DIR}/modules/dl_py2f)

set(GAMESS-UK_SRC_DIR ${GAMESS-UK})
set(GAMESS-UK_NAME gamess-uk-8.0)

if(NOT ${GAMESS-UK} STREQUAL "")
    get_filename_component(GAMESS-UK ${GAMESS-UK} ABSOLUTE)
endif()

#    # to make static lib linkable from a shared lib
#    set(GAMESS-UK_FFLAGS "${CMAKE_Fortran_FLAGS} -fPIC")
if(IS_DIRECTORY ${GAMESS-UK})

    message("Found GAMESS-UK in ${GAMESS-UK}")


    list(APPEND GAMESS-UK_STATIC_LIBS dft/libdft.a
                                      linalg/liblinalg.a
                                      m4/libgamess.a
                                      newscf/libnewscf.a)
    
    # not suitable to use add_custom_command() or ExternalProject_Add_Step() because we need to hardcode all the specific libs again; nor macro() can be used.
    set(GAMESS-UK_INSTALL_COMMAND ${CMAKE_COMMAND} -E time)
    foreach(GAMESS-UK_STATIC_LIB ${GAMESS-UK_STATIC_LIBS})
        set(GAMESS-UK_INSTALL_COMMAND ${GAMESS-UK_INSTALL_COMMAND} "COMMAND" ${CMAKE_COMMAND} -E copy ${GAMESS-UK_SRC_DIR}/${GAMESS-UK_STATIC_LIB} ${CHEMSH_GAMESS-UK_LIB_DIR})
    endforeach(GAMESS-UK_STATIC_LIB)
 
    # insert debug flag through configure.dat keywords rather than directly in FFLAGSI8
    if(DEBUG)
        set(GAMESS-UK_EXTRA_CONFIG_KEYWORD "giga debug")
#    else()
#        set(GAMESS-UK_EXTRA_CONFIG_KEYWORD "huge")
    endif()

    if(${CHEMSH_ARCH} STREQUAL "intel")
        # default configure command
        if(WITH_MPI)
            # write out a configure.dat file
            # YL 13/09/2021: scalapack is found needed in addition to mkl_scalapack otherwise wrong gradients are computed in parallel UHF with NEWSCF
            file(WRITE ${GAMESS-UK_SRC_DIR}/m4/configure.dat "${GAMESS-UK_COMPILER_ARCH} parallel base mpi mpiwrap newscf nbo chemshell qmmm scalapack mkl_scalapack ${GAMESS-UK_EXTRA_CONFIG_KEYWORD}")
            set(MKFILE x86_64-unknown-linux-gnu-parallel-intel15.0.mk)
        else()
            # write out a configure.dat file
            file(WRITE ${GAMESS-UK_SRC_DIR}/m4/configure.dat "intel15.0 serial base newscf nbo chemshell qmmm ${GAMESS-UK_EXTRA_CONFIG_KEYWORD}")
            set(MKFILE x86_64-unknown-linux-gnu-serial-intel15.0.mk)
        endif()

        if(NOT EXISTS ${GAMESS-UK_SRC_DIR}/config/${MKFILE}.BAK)
            # read in the original x86_64-unknown-linux-gnu-parallel/serial-intel15.0.mk and backup it
            file(READ ${GAMESS-UK_SRC_DIR}/config/${MKFILE} MACHINE.MK)
            file(RENAME ${GAMESS-UK_SRC_DIR}/config/${MKFILE} ${GAMESS-UK_SRC_DIR}/config/${MKFILE}.BAK)
        else()
            # only trust the original file
            file(READ ${GAMESS-UK_SRC_DIR}/config/${MKFILE}.BAK MACHINE.MK)
        endif()
        # hack m4/machine.mk to prevent configure obtaining default options from the file (by grepping "dopt")
        string(REPLACE "dopt" "CHEMSH_HACKED" MACHINE.MK.HACKED ${MACHINE.MK})
        # in case of non-mpiifort wrapper
        string(REPLACE "FC = mpiifort"   "FC = ${GAMESS-UK_COMPILER}"   MACHINE.MK.HACKED ${MACHINE.MK.HACKED})
        string(REPLACE "LD =  mpiifort"  "LD = ${GAMESS-UK_COMPILER}"   MACHINE.MK.HACKED ${MACHINE.MK.HACKED})
        string(REPLACE "FC90 = mpiifort" "FC90 = ${GAMESS-UK_COMPILER}" MACHINE.MK.HACKED ${MACHINE.MK.HACKED})
        # write out a new x86_64-unknown-linux-gnu-parallel/serial-intel15.0.mk file
        file(WRITE ${GAMESS-UK_SRC_DIR}/config/x86_64-unknown-linux-gnu-parallel-${GAMESS-UK_COMPILER_ARCH}.mk ${MACHINE.MK.HACKED})

        # default build command: -j won't work for GAMESS-UK
        if(DEBUG)
            set(GAMESS-UK_COMPILE_FLAGS "-fPIC")
        else()
            set(GAMESS-UK_COMPILE_FLAGS "-fPIC -diag-disable=810,5140,6843,7784,8291 -mcmodel=large")
        endif()

    # Cray Programming Environment (CPE) GNU (this is not the true gcc/gfortran!)
    elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
        # default configure command
        if(WITH_MPI)
            # write out a configure.dat file
            # YL 13/09/2021: scalapack is found needed in addition to mkl_scalapack otherwise wrong gradients are computed in parallel UHF with NEWSCF
            file(WRITE ${GAMESS-UK_SRC_DIR}/m4/configure.dat "${CHEMSH_ARCH} parallel mpi base newscf chemshell qmmm large scalapack mkl_scalapack ${GAMESS-UK_EXTRA_CONFIG_KEYWORD}")
        else()
            message(FATAL_ERROR " >>> ERROR: We do not yet support compiling serial GAMESS-UK for ${CHEMSH_ARCH}")
        endif()

    else()
        message(FATAL_ERROR " >>> ERROR: We do not yet support compiling GAMESS-UK for ${CHEMSH_ARCH}")
    endif()

    # hack m4/machine.mk to prevent configure obtaining default options from the file (by grepping "dopt")

    # UNIX-only!
    set(GAMESS-UK_CONFIGURE_COMMAND sh configure -s)

    set(GAMESS-UK_BUILD_COMMAND make FC=${CMAKE_Fortran_COMPILER} CC=${CMAKE_C_COMPILER} FFLAGSI8=${GAMESS-UK_COMPILE_FLAGS} CFLAGSI8=${GAMESS-UK_COMPILE_FLAGS} > make.log)

    # force to recompile (and reinstall)
    if(GAMESS-UK_RECOMPILE)

        # CMake stupidly only accepts 0 or 1 in ExternalProject_Add()
        set(GAMESS-UK_RECOMPILE 1)
        set(GAMESS-UK_CLEAN 1)
        set(GAMESS-UK_CLEAN_COMMAND make clean)

        file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)

    # does not recompile
    else()

        # existing compiled lib files in given GAMESS-UK directory
        foreach(GAMESS-UK_STATIC_LIB ${GAMESS-UK_STATIC_LIBS})
            if(EXISTS ${GAMESS-UK_SRC_DIR}/${GAMESS-UK_STATIC_LIB})
                list(APPEND GAMESS-UK_FOUND_LIBS ${GAMESS-UK_SRC_DIR}/${GAMESS-UK_STATIC_LIB})
            else()
                message("${GAMESS-UK}/${GAMESS-UK_STATIC_LIB} not found, omitting...")
            endif()
        endforeach(GAMESS-UK_STATIC_LIB)

        list(LENGTH GAMESS-UK_FOUND_LIBS NUMBER_GAMESS-UK_FOUND_LIBS)

        # count how many libs are already installed
        foreach(GAMESS-UK_LIB ${GAMESS-UK_STATIC_LIBS})
            get_filename_component(GAMESS-UK_LIB_FILENAME ${GAMESS-UK_LIB} NAME)
            if(EXISTS ${CHEMSH_GAMESS-UK_LIB_DIR}/${GAMESS-UK_LIB_FILENAME})
                list(APPEND GAMESS-UK_INSTALLED_LIBS ${CHEMSH_GAMESS-UK_LIB_DIR}/${GAMESS-UK_LIB_FILENAME})
            endif()
        endforeach(GAMESS-UK_LIB)
        list(LENGTH GAMESS-UK_INSTALLED_LIBS NUMBER_GAMESS-UK_INSTALLED_LIBS)

        # force to recompile if no pre-compiled libs found
        if(${NUMBER_GAMESS-UK_INSTALLED_LIBS} EQUAL 0 AND ${NUMBER_GAMESS-UK_FOUND_LIBS} EQUAL 0)
            set(GAMESS-UK_RECOMPILE 1)
            set(GAMESS-UK_CLEAN 1)
            set(GAMESS-UK_CLEAN_COMMAND make clean)
            file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)
            message("${BoldRed}GAMESS-UK_RECOMPILE${EndColour} set OFF but no static library file(s) found under ${GAMESS-UK_SCR_DIR}: will recompile GAMESS-UK")
        else()
            set(GAMESS-UK_CLEAN 0)
            set(GAMESS-UK_CLEAN_COMMAND "")
            # do nothing if there are no less installed libs than found libs
            if(NOT ${NUMBER_GAMESS-UK_INSTALLED_LIBS} LESS ${NUMBER_GAMESS-UK_FOUND_LIBS})
                set(GAMESS-UK_RECOMPILE 1)
                set(GAMESS-UK_CONFIGURE_COMMAND "")
                set(GAMESS-UK_BUILD_COMMAND "")
                set(GAMESS-UK_INSTALL_COMMAND "")
                message("${BoldRed}GAMESS-UK_RECOMPILE${EndColour} set OFF: will not recompile GAMESS-UK")
            # reinstall but do not recompile if there are more found libs than installed libs
            else()
                set(GAMESS-UK_RECOMPILE 1)
                set(GAMESS-UK_CONFIGURE_COMMAND "")
                set(GAMESS-UK_BUILD_COMMAND "")
                file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)
                message("${BoldRed}GAMESS-UK_RECOMPILE${EndColour} set OFF but new static library file(s) found under ${GAMESS-UK_SCR_DIR}: will reinstall GAMESS-UK libraries")
            endif()
        endif()

    endif()

    # CMake does not support multiple-files copying util v3.5, therefore we have to concatenate the copying commands
 
    include(ExternalProject)
    ExternalProject_Add(
        ${GAMESS-UK_NAME}
        PREFIX            ${CHEMSH_ARCH}
        STAMP_DIR         ${CHEMSH_ARCH}/stamp
        TMP_DIR           ${CHEMSH_ARCH}/tmp
        BUILD_ALWAYS      ${GAMESS-UK_RECOMPILE}
        SOURCE_DIR        ${GAMESS-UK}
#        URL               file://${GAMESS-UK}
        CONFIGURE_COMMAND ""
        DOWNLOAD_COMMAND  ""
        BUILD_COMMAND     ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --magenta --bold "Architecture for which GAMESS-UK is built: '${CHEMSH_ARCH}'"
        INSTALL_COMMAND   ""
    )

    # only do this if GAMESS-UK_RECOMPILE
    ExternalProject_Add_Step(
        ${GAMESS-UK_NAME} GAMESS-UK_CONFIGURE
        COMMENT           "Cleaning up previous binaries for '${GAMESS-UK_NAME}'..."
        ALWAYS            ${GAMESS-UK_RECOMPILE}
        DEPENDEES         configure
        COMMAND           ${GAMESS-UK_CONFIGURE_COMMAND}
        WORKING_DIRECTORY ${GAMESS-UK_SRC_DIR}
    )
    # only do this if GAMESS-UK_RECOMPILE
    ExternalProject_Add_Step(
        ${GAMESS-UK_NAME} GAMESS-UK_CLEAN
        COMMENT           "Cleaning up previous binaries for '${GAMESS-UK_NAME}'..."
        ALWAYS            ${GAMESS-UK_CLEAN}
        DEPENDEES         GAMESS-UK_CONFIGURE
        COMMAND           ${GAMESS-UK_CLEAN_COMMAND}
        WORKING_DIRECTORY ${GAMESS-UK_SRC_DIR}
    )
   
    # only do this if GAMESS-UK_RECOMPILE
    ExternalProject_Add_Step(
        ${GAMESS-UK_NAME} GAMESS-UK_BUILD
        COMMENT           "Building static library for '${GAMESS-UK_NAME}'..."
        ALWAYS            ${GAMESS-UK_RECOMPILE}
        # always preceded by GAMESS-UK_CONFIGURE
        DEPENDEES         GAMESS-UK_CLEAN
        COMMAND           ${GAMESS-UK_BUILD_COMMAND}
        WORKING_DIRECTORY ${GAMESS-UK_SRC_DIR}
    )
   
    # only do this if GAMESS-UK_RECOMPILE
    ExternalProject_Add_Step(
        ${GAMESS-UK_NAME} GAMESS-UK_INSTALL
        COMMENT           "Installing static library to ${CHEMSH_GAMESS-UK_LIB_DIR}"
        ALWAYS            ${COPY_GAMESS-UK_LIBS}
        # always preceded by GAMESS-UK_BUILD
        DEPENDEES         GAMESS-UK_BUILD
        # FIXME: haven't found a way to avoid duplicated hardcoding
        COMMAND           ${CMAKE_COMMAND} -E copy ${GAMESS-UK_SRC_DIR}/dft/libdft.a       ${CHEMSH_GAMESS-UK_LIB_DIR}
        COMMAND           ${CMAKE_COMMAND} -E copy ${GAMESS-UK_SRC_DIR}/linalg/liblinalg.a ${CHEMSH_GAMESS-UK_LIB_DIR}
        COMMAND           ${CMAKE_COMMAND} -E copy ${GAMESS-UK_SRC_DIR}/m4/libgamess.a     ${CHEMSH_GAMESS-UK_LIB_DIR}
        COMMAND           ${CMAKE_COMMAND} -E copy ${GAMESS-UK_SRC_DIR}/newscf/libnewscf.a ${CHEMSH_GAMESS-UK_LIB_DIR}
        WORKING_DIRECTORY ${GAMESS-UK_SRC_DIR}
    )

    # list the copied libs files
    set(GAMESS-UK_COPIED_LIBS "")
    foreach(GAMESS-UK_LIB ${GAMESS-UK_STATIC_LIBS})
        get_filename_component(GAMESS-UK_LIB_FILENAME ${GAMESS-UK_LIB} NAME)
        if(EXISTS ${CHEMSH_GAMESS-UK_LIB_DIR}/${GAMESS-UK_LIB_FILENAME})
            list(APPEND GAMESS-UK_COMPILED_LIBS ${CHEMSH_GAMESS-UK_LIB_DIR}/${GAMESS-UK_LIB_FILENAME})
            set(GAMESS-UK_COPIED_LIBS "${GAMESS-UK_COPIED_LIBS} ${CHEMSH_GAMESS-UK_LIB_DIR}/${GAMESS-UK_LIB_FILENAME}")
        endif()
    endforeach(GAMESS-UK_LIB)

    # if no lib files has been copied, force it
    list(LENGTH GAMESS-UK_COPIED_LIBS NUM_GAMESS-UK_COPIED_LIBS)
    if(${NUM_GAMESS-UK_COPIED_LIBS} EQUAL 0)
        set(COPY_GAMESS-UK_LIBS 1)
    endif()

    # compile libgamess-uk.so (containing only ChemShell 4 interface files)
    add_library(gamess-uk SHARED gamess-uk.f90
                                 gamess-uk_routines.f90
                                 ${LINKED})
                                 #chemsh_mains.f
                                 #chemsh_master.f
                                 #chemsh_parallel.f
                                 #chemsh_machscf.f

    # add_dependencies to make sure target_link_libraries is performed only after ExternalProject_Add_Step is finished
    add_dependencies(gamess-uk ${GAMESS-UK_NAME})

    set_target_properties(gamess-uk PROPERTIES COMPILE_FLAGS "${CMake_Fortran_FLAGS}")

    # compile shared library from static archives by inserting them between --whole-archive and --no-whole-archive
    # do NOT use ${GAMESS-UK_COPIED_LIBS} which can be empty
    # so that static libraries are built in rather than linked in
    set_target_properties(gamess-uk PROPERTIES LINK_FLAGS "-Wl,--export-dynamic                                      \
                                                           -Wl,--whole-archive                                       \
                                                           ${CHEMSH_GAMESS-UK_LIB_DIR}/libdft.a                      \
                                                           ${CHEMSH_GAMESS-UK_LIB_DIR}/liblinalg.a                   \
                                                           ${CHEMSH_GAMESS-UK_LIB_DIR}/libgamess.a                   \
                                                           ${CHEMSH_GAMESS-UK_LIB_DIR}/libnewscf.a                   \
                                                           -Wl,--no-whole-archive                                    \
                                                           -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/export.map \
                                                           ${CHEMSH_ScaLAPACK_FLAGS}                                 \
                                                           -Wl,--allow-multiple-definition")

    # link the static library agains libgamess-uk.so
    target_link_libraries(gamess-uk dl_py2f parallel)

    # revert m4/machine.mk
	# this method has a bug: once the build procedure fails, the .mk file remains hacked and the user will lose the original version
#    if(TARGET gamess-uk)
#        file(WRITE ${GAMESS-UK_SRC_DIR}/config/${MKFILE} ${MACHINE.MK})
#    endif()

else()

    if(NOT ${GAMESS-UK} STREQUAL "")
        message("GAMESS-UK not found in directory ${GAMESS-UK}. Skipping...")
    endif()

    # compile libgamess-uk.so (containing only ChemShell 4 interface files and dummy GAMESS-UK main routine)
    add_library(gamess-uk SHARED gamess-uk.f90 gamess-uk_routines.f90 gamess-uk_dummies.f90 ${NOTLINKED})

    # link the static library against libgamess-uk.so
    target_link_libraries(gamess-uk dl_py2f parallel)

endif()

