cmake_minimum_required(VERSION 3.14.5)

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

# enable CUDA if it is found on the system
include(../workaround_check_language.cmake) # see https://gitlab.kitware.com/cmake/cmake/issues/19013
_workaround_check_language(CUDA)
if(CMAKE_CUDA_COMPILER)
    # 2) Enable the CUDA language if you want to run your code on a CUDA-capable GPU. This
    #    must be done before calling `find_package(GridTools)`
    enable_language(CUDA)
endif()

# 3) find installed GridTools version
find_package(GridTools 2.0.0 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)
            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.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>)
        else()
            message(WARNING "OpenACC not supported. The Fortran CUDA example won't be compiled.")
        endif()
    endif()
endif()
