if(PROVIDER.CMA)
    set(pMR_PROVIDER_CMA PROVIDER.CMA)
    include(CheckFunctionExists)
    CHECK_FUNCTION_EXISTS(process_vm_writev HAVE_CMA)
    if(NOT HAVE_CMA)
        ERROR("Cross Memory Attach not available!")
    endif()

    add_subdirectory(providers/cma)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_CMA>)
    list(APPEND PROVIDER_LIST "CMA")
endif()

if(PROVIDER.MPI)
    set(pMR_PROVIDER_MPI PROVIDER.MPI)
    if(NOT ${BACKEND} STREQUAL "MPI")
        ERROR("MPI provider only available for backend MPI!")
    endif()

    if(MPI.PERSISTENT)
        list(APPEND MPI_CAPS "Persistent")
        add_definitions(-DMPI_PERSISTENT)
    endif()

    if(MPI.TAG.NARROW)
        list(APPEND MPI_CAPS "TagNarrow")
        add_definitions(-DMPI_TAG_NARROW)
    endif()

    add_subdirectory(providers/mpi)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_MPI>)
    list(APPEND PROVIDER_LIST "MPI")

    if(MPI_CAPS)
        TOSTRING(MPI_CAPS ${MPI_CAPS})
        INFO("MPI capabilities: ${MPI_CAPS}")
    endif()
endif()

if(PROVIDER.NULL)
    set(pMR_PROVIDER_NULL PROVIDER.NULL)
    if(NULL.WARN)
        add_definitions(-DNULL_WARN)
    endif()
    add_subdirectory(providers/null)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_NULL>)
    list(APPEND PROVIDER_LIST "null")
endif()

if(PROVIDER.NUMA)
    find_library(NUMA NAMES numa PATHS ENV LIBRARY_PATH)
    if(NOT NUMA)
        ERROR("Library NUMA not found!")
    endif()
    add_subdirectory(providers/numa)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_NUMA>)
    list(APPEND LIBRARIES ${NUMA})
    list(APPEND AUX_PROVIDER_LIST "NUMA")
endif()

if(PROVIDER.OFI)
    set(pMR_PROVIDER_OFI PROVIDER.OFI)
    find_library(FABRIC NAMES fabric PATHS ENV LIBRARY_PATH)
    if(NOT FABRIC)
        ERROR("Library fabric not found!")
    endif()

    if(NOT OFI.EP STREQUAL "MSG" AND NOT OFI.EP STREQUAL "RDM")
        ERROR("OFI: Unsupported endpoint type: ${OFI.EP}")
    endif()
    add_definitions(-DOFI_EP_${OFI.EP})

    if(OFI.EP STREQUAL "RDM")
        if(OFI.RDM.GLOBAL)
            list(APPEND OFI_CAPS "RDMGlobal")
            add_definitions(-DOFI_RDM_GLOBAL)
            if(OFI.RDM.GLOBAL.THREADSAFE)
                list(APPEND OFI_CAPS "RDMGlobalThreadSafe")
                add_definitions(-DOFI_RDM_GLOBAL_THREADSAFE)
            endif()
        else()
            if(NOT OFI.RDM.DIRECTED.RECEIVE)
                ERROR("OFI: Non-global RDM requires DirectedReceive.")
            endif()
        endif()
        if(OFI.RDM.DIRECTED.RECEIVE)
            list(APPEND OFI_CAPS "RDMDirectedReceive")
            add_definitions(-DOFI_RDM_DIRECTED_RECEIVE)
        endif()
    endif()

    if(OFI.RMA)
        list(APPEND OFI_CAPS "RMA")
        add_definitions(-DOFI_RMA)
        if(OFI.RMA.EVENT)
            list(APPEND OFI_CAPS "RMAEvent")
            add_definitions(-DOFI_RMA_EVENT)
            if(OFI.RMA.EVENT.NONZERO)
                list(APPEND OFI_CAPS "RMAEventNonZero")
                add_definitions(-DOFI_RMA_EVENT_NONZERO)
            endif()
        endif()
        if(OFI.RMA.TARGET.RX)
            list(APPEND OFI_CAPS "RMATargetRx")
            add_definitions(-DOFI_RMA_TARGET_RX)
        endif()
        if(OFI.RMA.CONTROL)
            list(APPEND OFI_CAPS "RMAControl")
            add_definitions(-DOFI_RMA_CONTROL)
        endif()
    else()
        if(OFI.NO.CONTROL)
            list(APPEND OFI_CAPS "NoControl")
            add_definitions(-DOFI_NO_CONTROL)
        endif()
    endif()

    if(OFI.POLL.SPIN)
        list(APPEND OFI_CAPS "PollSpin")
        add_definitions(-DOFI_POLL_SPIN)
    endif()

    if(OFI.CONTROL.PROGRESS.AUTO)
        list(APPEND OFI_CAPS "ControlProgressAuto")
        add_definitions(-DOFI_CONTROL_PROGRESS_AUTO)
    endif()

    if(OFI.DATA.PROGRESS.AUTO)
        list(APPEND OFI_CAPS "DataProgressAuto")
        add_definitions(-DOFI_DATA_PROGRESS_AUTO)
    endif()

    if(OFI.RM)
        list(APPEND OFI_CAPS "RM")
        add_definitions(-DOFI_RM)
    endif()

    if(OFI.MR.SCALABLE)
        list(APPEND OFI_CAPS "MRScalable")
        add_definitions(-DOFI_MR_SCALABLE)
    endif()

    add_subdirectory(providers/ofi)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_OFI>)
    list(APPEND LIBRARIES ${FABRIC})
    list(APPEND PROVIDER_LIST "OFI")

    if(OFI_CAPS)
        TOSTRING(OFI_CAPS ${OFI_CAPS})
        INFO("OFI: Endpoint ${OFI.EP} with capabilities: ${OFI_CAPS}")
    else()
        INFO("OFI: Endpoint ${OFI.EP}")
    endif()
endif()

if(PROVIDER.PSM2)
    set(pMR_PROVIDER_PSM2 PROVIDER.PSM2)
    find_library(PSM2 NAMES psm2 PATHS ENV LIBRARY_PATH)
    if(NOT PSM2)
        ERROR("Library psm2 not found!")
    endif()

    if(PSM2.THREAD.SAFE)
        list(APPEND PSM2_CAPS "ThreadSafe")
        add_definitions(-DPSM2_THREAD_SAFE)
    endif()

    if(PSM2.WRAPPER)
        INFO("PSM2 Wrapper library enabled. Use LD_PRELOAD to load libpmr_psm2_preload.so")
        get_filename_component(LD_PRELOAD "${CMAKE_INSTALL_PREFIX}/lib/libpmr_psm2_preload.so" ABSOLUTE)
        list(APPEND REQUIRED_EXPORTS "LD_PRELOAD=${LD_PRELOAD}")
        list(APPEND PSM2_CAPS "Wrapper")
    endif()

    if(PSM2.WRAPPER.BLOCK)
        list(APPEND PSM2_CAPS "WrapperBlock")
        add_definitions(-DPSM2_WRAPPER_BLOCK)
    endif()

    if(PSM2.WAIT.BLOCK)
        list(APPEND PSM2_CAPS "WaitBlock")
        add_definitions(-DPSM2_WAIT_BLOCK)
    endif()

    if(PSM2.PRINT.STATS)
        list(APPEND PSM2_CAPS "PrintStats")
        add_definitions(-DPSM2_PRINT_STATS)
    endif()

    if(PSM2.EAGER.THRESHOLD STREQUAL "")
        ERROR("PSM2: No eager threshold set")
    endif()
    add_definitions(-DPSM2_EAGER_THRESHOLD=${PSM2.EAGER.THRESHOLD})
    list(APPEND PSM2_CAPS "EagerThreshold=${PSM2.EAGER.THRESHOLD}")

    add_subdirectory(providers/psm2)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_PSM2>)
    list(APPEND LIBRARIES ${PSM2})
    list(APPEND PROVIDER_LIST "PSM2")

    if(PSM2_CAPS)
        TOSTRING(PSM2_CAPS ${PSM2_CAPS})
        INFO("PSM2 capabilities: ${PSM2_CAPS}")
    endif()
endif()

if(PROVIDER.SCIF)
    set(pMR_PROVIDER_SCIF PROVIDER.SCIF)
    find_library(SCIF NAMES scif PATHS ENV LIBRARY_PATH)
    if(NOT SCIF)
        ERROR("Library scif not found!")
    endif()
    add_subdirectory(providers/scif)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_SCIF>)
    list(APPEND LIBRARIES ${SCIF})
    list(APPEND PROVIDER_LIST "SCIF")
endif()

if(PROVIDER.SELF)
    set(pMR_PROVIDER_SELF PROVIDER.SELF)
    if(SELF.WARN)
        add_definitions(-DSELF_WARN)
    endif()
    add_subdirectory(providers/self)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_SELF>)
    list(APPEND PROVIDER_LIST "self")
endif()

if(PROVIDER.VERBS)
    set(pMR_PROVIDER_VERBS PROVIDER.VERBS)
    find_library(IBVERBS NAMES ibverbs PATHS ENV LIBRARY_PATH)
    if(NOT IBVERBS)
        ERROR("Library ibverbs not found!")
    endif()

    if(VERBS.RDMA)
        list(APPEND VERBS_CAPS "RMDA")
        add_definitions(-DVERBS_RDMA)
        if(VERBS.RDMA.CONTROL)
            list(APPEND VERBS_CAPS "RMDAControl")
            add_definitions(-DVERBS_RDMA_CONTROL)
        endif()
    endif()

    if(VERBS.MTU STREQUAL 256)
        add_definitions(-DVERBS_MTU=IBV_MTU_256)
    elseif(VERBS.MTU STREQUAL 512)
        add_definitions(-DVERBS_MTU=IBV_MTU_512)
    elseif(VERBS.MTU STREQUAL 1024)
        add_definitions(-DVERBS_MTU=IBV_MTU_1024)
    elseif(VERBS.MTU STREQUAL 2048)
        add_definitions(-DVERBS_MTU=IBV_MTU_2048)
    elseif(VERBS.MTU STREQUAL 4096)
        add_definitions(-DVERBS_MTU=IBV_MTU_4096)
    else()
        ERROR("verbs: Unsupported MTU size.")
    endif()
    list(APPEND VERBS_CAPS "MTU=${VERBS.MTU}")

    if(VERBS.INLINE STREQUAL "")
        ERROR("verbs: No max inline data size set")
    endif()
    add_definitions(-DVERBS_INLINE=${VERBS.INLINE})
    list(APPEND VERBS_CAPS "MaxInlineData=${VERBS.INLINE}")

    if(VERBS.ODP)
        include(CheckStructHasMember)
        check_struct_has_member("struct ibv_device_attr_ex" odp_caps infiniband/verbs.h HAVE_IBVERBS_ODP LANGUAGE C)
        if(HAVE_IBVERBS_ODP)
            list(APPEND VERBS_CAPS "ODP")
            add_definitions(-DVERBS_ODP)
        endif()
    endif()

    add_subdirectory(providers/verbs)
    list(APPEND PROVIDERS $<TARGET_OBJECTS:PROVIDER_VERBS>)
    list(APPEND LIBRARIES ${IBVERBS})
    list(APPEND PROVIDER_LIST "verbs")

    if(VERBS_CAPS)
        TOSTRING(VERBS_CAPS ${VERBS_CAPS})
        INFO("verbs capabilities: ${VERBS_CAPS}")
    endif()
endif()

TOSTRING(PROVIDER_STRING ${PROVIDER_LIST})
INFO("Enabled provider(s): ${PROVIDER_STRING}")

if(AUX_PROVIDER_LIST)
    TOSTRING(AUX_PROVIDER_STRING ${AUX_PROVIDER_LIST})
    INFO("Enabled auxiliary provider(s): ${AUX_PROVIDER_STRING}")
endif()
