cmake_minimum_required(VERSION 3.18.1)

# 1) GridTools needs the language CXX
project(GridTools-examples LANGUAGES CXX)

set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CUDA_EXTENSIONS OFF)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..")

# detect CUDA variant (Clang with CUDA support or NVCC)
include(detect_features)
detect_cuda_type(GT_CUDA_TYPE AUTO)

if(GT_CUDA_TYPE STREQUAL "NVCC-CUDA")
    # 2) Enable the CUDA language if you want to run your code on a CUDA-capable GPU.
    #    This needs to be done before find_package(GridTools) to properly setup the GridTools targets for CUDA.
    enable_language(CUDA)
endif()

# 3) find installed GridTools version
find_package(GridTools 2.3.2 REQUIRED
    HINTS /usr/lib/cmake/GridTools)

# 4) In the future cpp_bindgen will not be shipped with GridTools, then the following
#    steps are needed to make the library available
include(FetchContent)
FetchContent_Declare(
    cpp_bindgen
    GIT_REPOSITORY https://github.com/GridTools/cpp_bindgen.git
    GIT_TAG        v1.0.1
    )
FetchContent_MakeAvailable(cpp_bindgen)

enable_testing()

# 5) generate a bindings library for cpu_ifirst backend. This generates two targets:
#    - copy_stencil_lib_cpu_c (the C library)
#    - copy_stencil_lib_cpu_fortran (the Fortran Library)
bindgen_add_library(copy_stencil_lib_cpu SOURCES copy_stencil_wrapper.cpp)
target_link_libraries(copy_stencil_lib_cpu PUBLIC GridTools::stencil_cpu_ifirst)

include(CheckLanguage)
check_language(C)
if(CMAKE_C_COMPILER)
    enable_language(C)

    add_executable(example_driver_cpu_c driver_cpu.c)
    target_link_libraries(example_driver_cpu_c copy_stencil_lib_cpu_c)

    add_test(NAME example_driver_cpu_c COMMAND $<TARGET_FILE:example_driver_cpu_c>)
endif()

check_language(Fortran)
if(CMAKE_Fortran_COMPILER)
    enable_language(Fortran)
    # 6) If a library needs to be used from Fortran, it is necessary to call
    #    bindgen_enable_fortran_library with the bindings library in order to
    #    build the right modules
    bindgen_enable_fortran_library(copy_stencil_lib_cpu)

    add_executable(example_driver_fortran driver.F90)
    target_link_libraries(example_driver_fortran copy_stencil_lib_cpu_fortran)
    set_target_properties(example_driver_fortran PROPERTIES LINKER_LANGUAGE Fortran)
    add_test(NAME example_driver_fortran COMMAND $<TARGET_FILE:example_driver_fortran>)
endif()

if(TARGET GridTools::stencil_gpu)
    set(EXAMPLE_CUDA_ARCH "" CACHE STRING "CUDA compute capability to be used for this example.")

    bindgen_add_library(copy_stencil_lib_gpu SOURCES copy_stencil_wrapper.cpp)
    target_link_libraries(copy_stencil_lib_gpu PUBLIC GridTools::stencil_gpu)
    gridtools_setup_target(copy_stencil_lib_gpu CUDA_ARCH ${EXAMPLE_CUDA_ARCH})

    if(CMAKE_C_COMPILER_LOADED)
        add_executable(example_driver_gpu_c driver_gpu.c)
        target_link_libraries(example_driver_gpu_c copy_stencil_lib_gpu_c)
        add_test(NAME example_driver_gpu_c COMMAND $<TARGET_FILE:example_driver_gpu_c>)
    endif()

    if(CMAKE_Fortran_COMPILER_LOADED)
        if(CMAKE_Fortran_COMPILER_ID MATCHES "PGI")
            # FindOpenACC seems to be broken for modern versions of PGI, we assume PGI has OpenACC...
            set(OpenACC_Fortran_FLAGS -acc)
            set(OpenACC_Fortran_FOUND ON)
        else()
            find_package(OpenACC)
        endif()
        if(OpenACC_Fortran_FOUND)
            # OpenACC support in gfortran is fragile, by default we switch off compilation
            option(C_BINDING_ENABLE_GNU_OPENACC "Enable OpenACC example with the gfortran" OFF)
            if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" OR C_BINDING_ENABLE_GNU_OPENACC)
                bindgen_enable_fortran_library(copy_stencil_lib_gpu)
                target_compile_options(copy_stencil_lib_gpu PUBLIC $<$<COMPILE_LANGUAGE:Fortran>:${OpenACC_Fortran_FLAGS}>)
                target_link_options(copy_stencil_lib_gpu PUBLIC $<$<COMPILE_LANGUAGE:Fortran>:${OpenACC_Fortran_FLAGS}>)

                add_executable(example_driver_gpu_fortran driver_acc.F90)
                set_target_properties(example_driver_gpu_fortran PROPERTIES LINKER_LANGUAGE Fortran)
                target_link_libraries(example_driver_gpu_fortran copy_stencil_lib_gpu_fortran)
                add_test(NAME example_driver_gpu_fortran COMMAND $<TARGET_FILE:example_driver_gpu_fortran>)
            endif()
        else()
            message(WARNING "OpenACC not supported. The Fortran CUDA example won't be compiled.")
        endif()
    endif()
endif()
