#===============================================================================
#
# Common CMakeLists.txt: a framework for building all CIME components and more
#
# This is a port of cime/CIME/Tools/Makefile. As more components are ported to
# CMake, the directory level of this file will rise to the top-level directory.
#
# We will prefer space-separated strings over lists
#
#===============================================================================

cmake_minimum_required(VERSION 3.18)
cmake_policy(SET CMP0057 NEW)
cmake_policy(SET CMP0074 NEW)
cmake_policy(SET CMP0079 NEW) # Remove once scorpio in a better state
set(CMAKE_CXX_STANDARD 17)

# Turn on wrapper
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "python3 ${CASEROOT}/Tools/e3sm_compile_wrap.py ")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK    "python3 ${CASEROOT}/Tools/e3sm_compile_wrap.py ")

# We need to set the compilers *before* calling `project`.
# The only way to get the compiler name, is to load Macros.cmake
# However, we do *not* want to pollute the environment with other
# vars coming from Macros.cmake, so we encapsulate its inclusion
# in a new scope.
# Additionally, we also set CMAKE_BUILD_TYPE=DEBUG if Macros.cmake
# contains DEBUG set to true
function(set_compilers_e3sm)
  # Grab CXX compiler from CIME
  include(${CASEROOT}/Macros.cmake)

  if (DEBUG)
    set(E3SM_DEFAULT_BUILD_TYPE "DEBUG" CACHE STRING "Default build type, inferred from ${DEBUG}")
  else()
    set(E3SM_DEFAULT_BUILD_TYPE "RELEASE" CACHE STRING "Default build type, inferred from ${DEBUG}")
  endif()

  set(CMAKE_CXX_COMPILER     ${CMAKE_CXX_COMPILER}     CACHE STRING "The CXX compiler")
  set(CMAKE_C_COMPILER       ${CMAKE_C_COMPILER}       CACHE STRING "The C   compiler")
  set(CMAKE_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} CACHE STRING "The Fortran compiler")

  # USE_CUDA or USE_HIP is set through Macros.cmake
  # For instance: cime_config/machines/cmake_macros/gnugpu_summit.cmake
  # If it exists, then set parent's scope to true; otherwise to false
  # At this point, we use either CUDA or HIP.
  # Revisit as needed for future systems.
  if (USE_CUDA)
    set(USE_CUDA TRUE PARENT_SCOPE)
  elseif (USE_HIP)
    set(USE_HIP TRUE PARENT_SCOPE)
  else()
    set(USE_CUDA FALSE PARENT_SCOPE)
    set(USE_HIP FALSE PARENT_SCOPE)
  endif()
endfunction()
set_compilers_e3sm()

# If no CMAKE_BUILD_TYPE/CMAKE_CONFIGURATION_TYPES are provided,
# then set the build type according to the DEBUG variable found in Macros.cmake

# Why do we CMAKE_BUILD_TYPE to be set? Glad you asked. Read on.
# Some subfolders might need it (or even try to set it based on
# Macros.cmake anyways). However, not setting it GLOBALLY
# has the risk that cmake may treat targets differently in different subfolders.
# One BIG exapmle: the yaml-cpp target appends a 'd' to its file name when
# the build type is DEBUG (i.e., the lib name is libyaml-cppd.a).
# If yaml-cpp is built in a subdir that locally sets CMAKE_BUILD_TYPE=DEBUG,
# cmake will create the yaml-cpp target to point to libyaml-cppd.a.
# However, in other directories where CMAKE_BUILD_TYPE is not
# set (or not set to DEBUG), cmake will try to  link libyaml-cpp.a
# Bottom line: just like CMAKE_<LANG>_COMPILER, you SHOULD set
# CMAKE_BUILD_TYPE at most once, and do it BEFORE calling `project`.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)

  message(STATUS "Setting build type to '${E3SM_DEFAULT_BUILD_TYPE}', as none was specified. This default comes from what CIME set in the DEBUG variable")
  set(CMAKE_BUILD_TYPE "${E3SM_DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)

  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "DEBUG" "RELEASE")
endif()

project(E3SM C CXX Fortran)

if (COMP_INTERFACE STREQUAL "moab")
  set(USE_MOAB True)
  set(CPPDEFS "${CPPDEFS} -DHAVE_MOAB")
endif()

if (USE_CUDA)
  set(CMAKE_CUDA_COMPILER_FORCED True)
  enable_language(CUDA)
elseif (USE_HIP)
  set(CMAKE_HIP_COMPILER_FORCED True)
  enable_language(HIP)
endif()

# Any changes to SourceMods will require us to reconfigure
file(GLOB COMPONENT_SOURCE_MOD_DIRS "${CASEROOT}/SourceMods/src.*")
foreach(COMPONENT_SOURCE_MOD_DIR IN LISTS COMPONENT_SOURCE_MOD_DIRS)
  set_property(
    DIRECTORY
    APPEND
    PROPERTY CMAKE_CONFIGURE_DEPENDS
    ${COMPONENT_SOURCE_MOD_DIR})
endforeach()

# Include function definitions
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_util.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_mpas_model.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_eamxx.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_model.cmake)

# Set up CMAKE_MODULE_PATH so any component can use E3SM
# and CIME cmake modules if they want.
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
list(APPEND CMAKE_MODULE_PATH ${CIMEROOT}/CIME/non_py/src/CMake)

set(CMAKE_VERBOSE_MAKEFILE TRUE)

# Find dependencies
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/find_dep_packages.cmake)

# Scream manages its own flags
build_eamxx()

set(BUILDCONF ${CASEROOT}/Buildconf)

build_mpas_models()

# Set global cmake settings
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/..)

# Set global targets
if (NOT TARGET genf90)
  add_custom_target(genf90
    DEPENDS ${CIMEROOT}/CIME/non_py/externals/genf90/genf90.pl)
endif()

# Build E3SM components
set(IDX 0)
set(COMP_CLASSES cpl atm lnd ice ocn rof glc wav iac esp)
set(SKIP_COMPS "scream" "mpaso" "mpassi" "mali")
foreach(COMP_NAME IN LISTS COMP_NAMES)
  list(GET COMP_CLASSES ${IDX} COMP_CLASS)
  if (NOT COMP_CLASS STREQUAL "cpl" AND NOT COMP_NAME IN_LIST SKIP_COMPS)
    message("Found component ${COMP_CLASS} model '${COMP_NAME}'")
    add_subdirectory(cmake/${COMP_CLASS})
  endif()

  math(EXPR IDX "${IDX} + 1")
endforeach()

# Build the E3SM coupler and exe
add_subdirectory(cmake/cpl)
