set(SRC_F_devXlib
    # Linalg
    devxlib_linalg.f90
    devxlib_linalg_axpy.f90
    devxlib_linalg_dot.f90
    devxlib_linalg_gemm.f90
    devxlib_linalg_gemv.f90
    # Malloc
    devxlib_malloc.f90
    devxlib_malloc_alloc.f90
    devxlib_malloc_free.f90
    devxlib_malloc_allocated.f90
    # Mapping
    devxlib_mapping.f90
    devxlib_mapping_map.f90
    devxlib_mapping_unmap.f90
    devxlib_mapping_mapped.f90
    # Memcpy
    devxlib_memcpy.f90
    devxlib_memcpy_d2d.f90
    devxlib_memcpy_d2h.f90
    devxlib_memcpy_h2d.f90
    devxlib_memcpy_h2h.f90
    # Auxfunc
    devxlib_auxfunc.f90
    devxlib_auxfunc_conjg.f90
    devxlib_auxfunc_vec_upd.f90
    devxlib_auxfunc_mat_upd.f90
    devxlib_auxfunc_addscal.f90
    # Buffers
    devxlib_buffer.f90
    devxlib_pinned.f90
    devxlib_buffers.f90
    # Memset
    devxlib_memset_device.f90
    devxlib_memset.f90
    # Main modules
    devxlib_environment.f90
    devxlib.f90
    # CUDA
    devxlib_cublas.f90
    mod_cublas_core.f90
    mod_cublas.f90
    # ROCm
    devxlib_rocblas.f90
    mod_rocblas.f90
    # Tests
    mod_tester_fbuff.f90
    # Misc
    mod_blas95.f90)
if(DEVXLIB_ENABLE_ACC STREQUAL "CUDAF")
    set(SRC_F_devXlib
        ${SRC_F_devXlib}
        devxlib_memset_host.f90)
endif()
if(DEVXLIB_ENABLE_GPU_BLAS STREQUAL "MKLGPU")
    set(SRC_F_devXlib
        ${SRC_F_devXlib}
        devxlib_mklgpu.f90)
endif()

set(SRC_F_TEST_BUFF test_buffer.f90)
set(SRC_F_TEST_PINN test_pinned.f90)
set(SRC_F_TEST_LINALG test_linalg.f90)
set(SRC_F_TEST_MEMCPY test_memcpy.f90)
set(SRC_F_TEST_MEMCPY_ASYNC test_memcpy_async.f90)
set(SRC_F_TEST_MALLOC test_malloc.f90)
set(SRC_F_TEST_MAPPING test_mapping.f90)

set_source_files_properties(
    ${SRC_F_devXlib}
    ${SRC_F_TEST_BUFF}
    ${SRC_F_TEST_PINN}
    ${SRC_F_TEST_LINALG}
    ${SRC_F_TEST_MEMCPY}
    ${SRC_F_TEST_MEMCPY_ASYNC}
    ${SRC_F_TEST_MALLOC}
    ${SRC_F_TEST_MAPPING}
    PROPERTIES LANGUAGE Fortran)

set_source_files_properties(
    ${SRC_F_devXlib}
    ${SRC_F_TEST_BUFF}
    ${SRC_F_TEST_PINN}
    ${SRC_F_TEST_LINALG}
    ${SRC_F_TEST_MEMCPY}
    ${SRC_F_TEST_MEMCPY_ASYNC}
    ${SRC_F_TEST_MALLOC}
    ${SRC_F_TEST_MAPPING}
    PROPERTIES Fortran_PREPROCESS ON)
set_source_files_properties(
    ${SRC_F_devXlib}
    ${SRC_F_TEST_BUFF}
    ${SRC_F_TEST_PINN}
    ${SRC_F_TEST_LINALG}
    ${SRC_F_TEST_MEMCPY}
    ${SRC_F_TEST_MEMCPY_ASYNC}
    ${SRC_F_TEST_MALLOC}
    ${SRC_F_TEST_MAPPING}
    PROPERTIES CMAKE_Fortran_FORMAT FREE)

add_library(devXlib ${SRC_F_devXlib})
add_executable(test_buffer ${SRC_F_TEST_BUFF})
add_executable(test_pinned ${SRC_F_TEST_PINN})
add_executable(test_linalg ${SRC_F_TEST_LINALG})
add_executable(test_memcpy ${SRC_F_TEST_MEMCPY})
add_executable(test_memcpy_async ${SRC_F_TEST_MEMCPY_ASYNC})
add_executable(test_malloc ${SRC_F_TEST_MALLOC})
add_executable(test_mapping ${SRC_F_TEST_MAPPING})

set(testTargets
    test_buffer
    test_pinned
    test_linalg
    test_memcpy
    test_memcpy_async
    test_malloc
    test_mapping)
set(devXlibTargets
    devXlib
    ${testTargets})

set_target_properties(test_buffer
    PROPERTIES OUTPUT_NAME "test_buffer.x")
set_target_properties(test_pinned
    PROPERTIES OUTPUT_NAME "test_pinned.x")
set_target_properties(test_linalg
    PROPERTIES OUTPUT_NAME "test_linalg.x")
set_target_properties(test_memcpy
    PROPERTIES OUTPUT_NAME "test_memcpy.x")
set_target_properties(test_memcpy_async
    PROPERTIES OUTPUT_NAME "test_memcpy_async.x")
set_target_properties(test_malloc
    PROPERTIES OUTPUT_NAME "test_malloc.x")
set_target_properties(test_mapping
    PROPERTIES OUTPUT_NAME "test_mapping.x")

target_link_libraries(devXlib
    PRIVATE
        LAPACK::LAPACK
        compilerCustomConfig)
foreach(TARGET ${testTargets})
    target_link_libraries(${TARGET}
        PRIVATE
            devXlib
            LAPACK::LAPACK
            compilerCustomConfig)
endforeach(TARGET)

foreach(TARGET ${devXlibTargets})
    target_include_directories(${TARGET} 
        PRIVATE
            "${CMAKE_SOURCE_DIR}/include"
            "${CMAKE_CURRENT_SOURCE_DIR}")

    if(DEVXLIB_ENABLE_ACC STREQUAL "CUDAF")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_CUDAF")
    elseif(DEVXLIB_ENABLE_ACC STREQUAL "OPENACC")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_OPENACC")
        target_link_libraries(${TARGET}
            PRIVATE
                OpenACC::OpenACC_Fortran)
    elseif(DEVXLIB_ENABLE_ACC STREQUAL "OPENMPGPU")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_OPENMP_GPU")
        # Linking for OpenMP host
        target_link_libraries(${TARGET}
        	INTERFACE
        		OpenMP::OpenMP_Fortran
                OpenMP::OpenMP_C)
    endif()

    if(DEVXLIB_ENABLE_GPU_BLAS STREQUAL "CUBLAS")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_CUBLAS")
        target_link_libraries(${TARGET}
            PRIVATE
                "${CUDA_CUDART}"
                "${CUDA_cublas_LIBRARY}")
    elseif(DEVXLIB_ENABLE_GPU_BLAS STREQUAL "MKLGPU")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_MKL_GPU")
        target_include_directories(${TARGET}
            PRIVATE
                ${MKL_H})
    elseif(DEVXLIB_ENABLE_GPU_BLAS STREQUAL "ROCBLAS")
        target_compile_definitions(${TARGET}
            PRIVATE
                "__DXL_ROCBLAS")
        target_link_libraries(${TARGET}
            PRIVATE
                roc::rocblas)
    endif()
endforeach(TARGET)

install(
    TARGETS ${devXlibTargets}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
