###########################################################
#
# MAIN CMAKE FILE FOR GMIN 
# ========================
#
# Usage: 
#   
#   $ mkdir ~/svn/GMIN/builds/pgf90
#   $ cd ~/svn/GMIN/builds/pgf90
#   $ FC=pgf90 cmake ../../source 
#   $ make -j8 <target>    
#              -- if <target> not specified, all active ones are built  
#
# Good to know: 
#   1. VERBOSE=1 make   
#         -- gives the full compilation command for each file, useful for
#         debugging  
#
#   2. To compile with MPI 
#         FC=mpif90 cmake ../source -DCOMPILER_SWITCH=pgi 
#         make GMIN 
#       
#   3. GUI for CMakeCache.txt  
#         ccmake .    (after running cmake command) 
#         e.g.: 
#              set WITH_MPI to ON -> Press [c] -> Press [e] -> Press [g]  
#           equivalent command line version
#            $ cmake . -DWITH_MPI=yes 
# 
#   4. Out-of-source build only 
#         do not run cmake in ~svn/GMIN/source    
#
#   5. Tested with 
#        $ cmake --version 
#        cmake version 2.8.7.20120416-g31e7
#
#   6. To do a clean build, first delete CMakeCache.txt, and if that doesn't
#   help, delete the whole directory!
# 
############################################################

cmake_minimum_required(VERSION 2.8)
# 2.8 required particularly for the charmm-interfaced targets.

if (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${CMAKE_BINARY_DIRECTORY}$")
  message( FATAL_ERROR "CMake should not be built in the source directory" )
endif (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${CMAKE_BINARY_DIRECTORY}$")

project(GMIN)

set(PROJECT_VERSION "0.0")
set(PROJECT_CONTACT "dw34@cam.ac.uk")
get_filename_component(SVN_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE)

# Cmake modules/macros are in a subdirectory to keep this file cleaner
# CMake modules implement some additional functionality to check
# for external dependencies
set(CMAKE_MODULE_PATH ${SVN_ROOT}/CMakeModules)

# if not specified by user, the standard build type is release
if(NOT CMAKE_BUILD_TYPE)
  #Release comes with -O3 by default
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif(NOT CMAKE_BUILD_TYPE)

enable_language(CXX)

# the FORTRANCOMPILER check is in svn/CMakeModules
# it adds a compiler switch and sets flags
find_package(FORTRANCOMPILER)

message("CMAKE_Fortran_COMPILER = ${CMAKE_Fortran_COMPILER}")
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/modules)
include_directories(${CMAKE_CURRENT_BINARY_DIR}/modules)


include_directories(${CMAKE_SOURCE_DIR})
include(${CMAKE_SOURCE_DIR}/PythonGMIN.cmake)

file(GLOB DUMMY_AMH amhdummy_cmake.f)
file(GLOB DUMMY_CHARMM chdummy.f)
file(GLOB DUMMY_AMBER9 amber9dummy.f90)
file(GLOB DUMMY_USERPOT dummy_userpot.f90)
file(GLOB DUMMY_DMACRYS dummy_dmacrys.f90)
file(GLOB DUMMY_TESTING dummy_testing.f90)
file(GLOB DUMMY_AMBER12 amber12_dummies/*.F90)
file(GLOB DUMMY_CUDA modcudadummy.f90)

file(GLOB GMIN_MAIN main.F)
file(GLOB GMIN_PREPROCESS io1.F mylbfgs.F)
file(GLOB GMIN_SOURCES *.f *.f90 *.F *.F90 ./AMH/amhglobals.f ./AMH/amh_interfaces.f90 ./AMH/E_write.f90)

# Due to a compiler bug in ifort 13.1.3, we can't use -O3 for genrigid.f90
# Investigations continue...
if( ${COMPILER_SWITCH} STREQUAL "ifort" )
  SET_SOURCE_FILES_PROPERTIES( genrigid.f90 PROPERTIES COMPILE_FLAGS -O2 )
endif ( ${COMPILER_SWITCH} STREQUAL "ifort" )

file(GLOB NOT_GMIN_SOURCES
    dummyoptim.f90 # we need to do this separately dependency hell!	
    molinfo.f90 tetherfuncall.f90 transforms.f90 olami.f rdpot.f wenzel.1D.f wenzel.2D.f compress2.f 
    mf.f otp.f bmin.f evstep.f lbfgs.f takestep2.f ptbs.f io_sanity.f90 fsc1TH.f90 dblpy.f90 funcs.f90
    dfuncs.f90 io.f90 mek-quake.f90 wham_djw.f90 dgetf2.f  dtrti2.f charmmBildc.f blas.f
    lapack.f dtrtri.f mind.f nonATLASblas.f dlaswp.f  symmetry3.f90 bipartite.f90    
    dlamch.f myblas.f mylapack.f rk45ad.f symmetry2.f90 tn.f amberdummy.f amhdummy.f
    bsptsave.F *.F.f ptmc.F gmin_quip_wrapper.f90 
    # remove these in case already exist. will be added later
    display_version.f90 porfuncs.f90 pygmin.F modcudalbfgs.f90
)

list(REMOVE_ITEM GMIN_SOURCES ${NOT_GMIN_SOURCES} ${DUMMY_AMH} ${DUMMY_CHARMM} ${DUMMY_AMBER9}  ${DUMMY_DMACRYS} ${DUMMY_USERPOT} ${DUMMY_TESTING} ${DUMMY_AMBER12} ${DUMMY_CUDA})

# ---- add generated source files 
set(GMIN_SOURCES ${GMIN_SOURCES} display_version.f90 porfuncs.f90)

# ---- porfuncs.f90 TODO: shuold it build every time?
add_custom_command (
   OUTPUT porfuncs.f90 
   COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/porfuncs.sh ${COMPILER_SWITCH} > porfuncs.f90)

set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES porfuncs.f90)

# ---- display_version.f90 
add_custom_command (
  OUTPUT display_version.f90 
  COMMAND bash ${SVN_ROOT}/SCRIPTS/all/display_version.sh > display_version.f90
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES display_version.f90)

# collect all the extra source files into one place
set(ALL_EXTRA_SOURCES ${GMIN_MAIN} ${GMIN_PREPROCESS} ${DUMMY_CHARMM} ${DUMMY_AMH} ${DUMMY_AMBER9} ${DUMMY_DMACRYS} ${DUMMY_USERPOT} ${DUMMY_TESTING} ${DUMMY_AMBER12} ${DUMMY_CUDA})


# ----- Libraries 
# ----- generate a gmin library to avoid recompiling the whole code for every executable
add_library(gminlib ${GMIN_SOURCES})
add_library(dummylib dummyoptim.f90)
ADD_DEPENDENCIES(dummylib gminlib)
add_library(amber12dummylib ${DUMMY_AMBER12})
add_library(cudadummylib ${DUMMY_CUDA})

find_package(MYBLAS)
find_package(MYLAPACK)

# MPI flag 
option(WITH_MPI "Enable MPI compilation" OFF)
if(WITH_MPI)
  # append -DMPI to fortran flags 
  set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -DMPI")  
  message("building with MPI: \n  ${CMAKE_Fortran_FLAGS}")   
endif(WITH_MPI)

add_definitions(-D_SVN_ROOT_='${SVN_ROOT}')

# --- Finally, the execs

# standard GMIN
add_executable(GMIN ${ALL_EXTRA_SOURCES})
target_link_libraries(gminlib amber12dummylib cudadummylib)
target_link_libraries(GMIN gminlib ${MYLAPACK_LIBS})
set_target_properties(GMIN PROPERTIES LINKER_LANGUAGE "Fortran") 
# Above needed for when GMIN compiled during CUDAGMIN compilation, otherwise C++ linker is chosen

# AMHGMIN
option(WITH_AMH "Enable AMHGMIN compilation" OFF)
if(WITH_AMH)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_AMH})
  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/AMH)
  add_executable(AMHGMIN ${EXTRA_SOURCES})
  target_link_libraries(AMHGMIN gminlib amh ${MYLAPACK_LIBS})
endif(WITH_AMH)

# A9GMIN  
option(WITH_AMBER9 "Enable Amber GMIN with AMBER9 compilation" OFF)
if(WITH_AMBER9)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_AMBER9})
  find_package(NAB)
  add_subdirectory(AMBER) # temporary folder to copy files from sander;  
  add_executable(A9GMIN ${EXTRA_SOURCES})
  target_link_libraries(A9GMIN gminlib AMBER_LIB NAB_LIB dummylib ${MYLAPACK_LIBS})
endif(WITH_AMBER9)

# For A12GMIN or CUDAGMIN, we need to build and add the gmin12lib library, which doesn't link against amber12dummylib.
if(WITH_AMBER12 OR WITH_CUDA)
  add_library(gmin12lib ${GMIN_SOURCES})
endif()

# A12GMIN  
option(WITH_AMBER12 "Enable Amber GMIN with AMBER12 compilation" OFF)
if(WITH_AMBER12)
  add_subdirectory(AMBER12) # temporary folder to copy files from sander;  
                        # TODO: make this folder hidden or consider moving sander files locally 
  add_executable(A12GMIN ${GMIN_MAIN} ${GMIN_PREPROCESS} ${DUMMY_CHARMM} ${DUMMY_AMBER9} ${DUMMY_AMH} ${DUMMY_DMACRYS} ${DUMMY_USERPOT} ${DUMMY_TESTING} ${DUMMY_CUDA})
  set_target_properties(A12GMIN PROPERTIES LINKER_LANGUAGE "Fortran") 
  target_link_libraries(gmin12lib amber12)
  target_link_libraries(A12GMIN amber12 gmin12lib dummylib ${MYLAPACK_LIBS})
endif(WITH_AMBER12)

# CUDAGMIN
option(WITH_CUDA "Enable CUDAGMIN compilation" OFF)
if(WITH_CUDA)
  if(NOT ${COMPILER_SWITCH} STREQUAL "ifort" AND NOT ${COMPILER_SWITCH} STREQUAL "gfortran")
    message( FATAL_ERROR "The cuda version of AMBER can only be compiled with gnu or Intel compilers. " )
  endif (NOT ${COMPILER_SWITCH} STREQUAL "ifort" AND NOT ${COMPILER_SWITCH} STREQUAL "gfortran") 
  add_subdirectory(AMBER12CUDA)
  add_subdirectory(cuda_lbfgs/projects)
  add_subdirectory(cuda_lbfgs/lbfgs)
  add_executable(CUDAGMIN ${GMIN_MAIN} ${GMIN_PREPROCESS} ${DUMMY_CHARMM} ${DUMMY_AMBER9} ${DUMMY_AMH} ${DUMMY_DMACRYS} ${DUMMY_USERPOT} ${DUMMY_TESTING})
  add_library(modcuda modcudalbfgs.f90)
  ADD_DEPENDENCIES(modcuda gmin12lib)
  set_target_properties(CUDAGMIN PROPERTIES LINKER_LANGUAGE "Fortran")
  target_link_libraries(gmin12lib cudaamber12) 
  target_link_libraries(CUDAGMIN cudaamber12 gmin12lib dummylib ${MYLAPACK_LIBS} modcuda cuda1 cuda2)
  set_target_properties(CUDAGMIN PROPERTIES COMPILE_DEFINITIONS "CUDA")
endif(WITH_CUDA)

#DMACRYS
option(WITH_DMACRYS "Enable DMAGMIN compilation (DMACRYS needs to be present!)" OFF)
if(WITH_DMACRYS)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_DMACRYS})
  add_subdirectory(DMACRYSinterface)
  add_executable(DMAGMIN ${EXTRA_SOURCES})
  target_link_libraries(DMAGMIN gminlib dmacrysinterface ${MYLAPACK_LIBS})
endif(WITH_DMACRYS)

#OXDNA
option(WITH_OXDNA "Enable OXDNAGMIN compilation" OFF)
if(WITH_OXDNA)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_USERPOT})
  add_subdirectory(OXDNAinterface)
  add_executable(OXDNAGMIN ${EXTRA_SOURCES})
  target_link_libraries(OXDNAGMIN gminlib OXDNA ${MYLAPACK_LIBS})
endif(WITH_OXDNA)

#SPIN interface
option(WITH_SPIN "Enable SPINGMIN compilation" OFF)
if(WITH_SPIN)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_USERPOT})
  add_subdirectory(SPINinterface)
  add_executable(SPINGMIN ${EXTRA_SOURCES})
  target_link_libraries(SPINGMIN gminlib SPIN ${MYLAPACK_LIBS})
endif(WITH_SPIN)

#OPEP interface
option(WITH_OPEP "Enable OPEPGMIN compilation" OFF)
if(WITH_OPEP)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_USERPOT})
  add_subdirectory(OPEPinterface)
  add_executable(OPEPGMIN ${EXTRA_SOURCES})
  target_link_libraries(OPEPGMIN gminlib OPEP ${MYLAPACK_LIBS})
endif(WITH_OPEP)

# C35GMIN 
option(WITH_CHARMM35 "Enable C35GMIN compilation" OFF)
if(WITH_CHARMM35)
  if(WITH_CHARMM36)
    message( FATAL_ERROR "cannot build CHARMM35 and CHARMM36 at the same time" )
  endif(WITH_CHARMM36)
  SET(EXTRA_SOURCES ${ALL_EXTRA_SOURCES})
  list(REMOVE_ITEM EXTRA_SOURCES ${DUMMY_CHARMM})
  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/CHARMMboth)
  add_executable(C35GMIN myblas.f mylapack.f ${EXTRA_SOURCES})
  # add the CHARMM library as a dependency
  add_dependencies(C35GMIN CHARMM_WAS_BUILT)
  # Link in the CHARMM libraries and the CHARMM35 GMIN interface
  target_link_libraries(C35GMIN gminlib mylapack myblascharmm charmm35_interface_lib ${CHARMM_LIBS})
#${DUMMY_DMACRYS}  ${DUMMY_USERPOT}  ${DUMMY_TESTING} ${DUMMY_AMBER12}  ${CHARMM_LIBS} gfortran)
endif(WITH_CHARMM35)

# C36GMIN, doesn't do anything just yet, just here as a template 
option(WITH_CHARMM36 "Enable C36GMIN compilation" OFF)
if(WITH_CHARMM36)
  message( FATAL_ERROR "CHARMM36 not yet implemented with CMake" )
#  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/CHARMMboth)
#  add_executable(C36GMIN ${GMIN_MAIN} ${GMIN_PREPROCESS} myblas.f mylapack.f ${DUMMY_AMBER9} ${DUMMY_AMH}
#${DUMMY_DMACRYS} ${DUMMY_USERPOT} ${DUMMY_TESTING} ${DUMMY_AMBER12} ${DUMMY_CUDA})
#  target_link_libraries(C36GMIN gminlib mylapack myblascharmm charmm ${CHARMM_LIBS})
endif(WITH_CHARMM36)

# Example section for USERPOT entry, please do not commit CMAkelists.txt if you make changes here!
# I'm working on a more general solutioin where you can give the path to the userpotenital source
# directory
# add_subdirectory(path/to/userpot/library)
# add_executable(USERGMIN main.F.f ${DUMMY_CHARMM} ${DUMMY_AMH} ${DUMMY_AMBER9} ${DUMMY_DMACRYS})
# target_link_libraries(GMIN gminlib ${MYLAPACK_LIBS} USERPOTLIBRARY <whatever else is needed>)

# Testing framework
option(WITH_TESTING "Enable automatic generation of tests (Testing folder has to be present!)" OFF)
if(WITH_TESTING)
  enable_testing()
  add_subdirectory(Testing)
endif(WITH_TESTING)

gmin_python_interface()

# Test for duplicates.
# In the standard build we first merge the object files together in a library
# (gminlib) to avoid duplicate compilation for multiple targets.  A
# drawback of this is that duplicate symbols (duplicate names of subroutines,
# functions, etc.) are simply ignored.  This is a separate target which builds
# GMIN directly from the sources, specifically to check for duplicate
# symbols.
option(WITH_DUPLICATE_TESTING "Compile GMIN in such a way that duplicate symbols are flagged" OFF)
if(WITH_DUPLICATE_TESTING)
  add_executable(GMIN_TEST_DUPLICATES ${GMIN_SOURCES} ${ALL_EXTRA_SOURCES})
  target_link_libraries(GMIN_TEST_DUPLICATES amber12dummylib cudadummylib ${MYLAPACK_LIBS})
  add_dependencies(GMIN_TEST_DUPLICATES gminlib) # so I don't have to manually specify all the other dependencies
endif(WITH_DUPLICATE_TESTING)
