# ============================================================================ #
# Copyright (c) 2026 NVIDIA Corporation & Affiliates.                          #
# All rights reserved.                                                         #
#                                                                              #
# This source code and the accompanying materials are made available under     #
# the terms of the Apache License 2.0 which accompanies this distribution.     #
# ============================================================================ #

# GPU RoCE bridge and HSB playback tools
# ==============================================================================
# These targets are gated by CUDAQ_REALTIME_ENABLE_HSB_TOOLS and require
# a pre-built holoscan-sensor-bridge (HSB) with DOCA support.
# They are NOT CI tests -- they need FPGA hardware or an FPGA emulator.

if (NOT HOLOSCAN_SENSOR_BRIDGE_SOURCE_DIR)
  message(FATAL_ERROR
    "HOLOSCAN_SENSOR_BRIDGE_SOURCE_DIR must be set when building HSB tools.")
endif()
if (NOT HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR)
  message(FATAL_ERROR
    "HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR must be set when building HSB tools.")
endif()

find_package(Threads REQUIRED)
find_package(CUDAToolkit REQUIRED)

# --------------------------------------------------------------------------- #
# Find HSB core library
# --------------------------------------------------------------------------- #

find_library(HSB_CORE_LIB
  NAMES hololink_core
  PATHS
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/src/hololink/core"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/lib"
  NO_DEFAULT_PATH)

if (NOT HSB_CORE_LIB)
  message(FATAL_ERROR
    "Could not find hololink_core library under ${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}.")
endif()

# --------------------------------------------------------------------------- #
# Find GPU RoCE Transceiver library
# --------------------------------------------------------------------------- #

find_library(GPU_ROCE_TRANSCEIVER_LIB
  NAMES gpu_roce_transceiver
  PATHS
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/src/hololink/operators/gpu_roce_transceiver"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/lib"
  NO_DEFAULT_PATH)

if (NOT GPU_ROCE_TRANSCEIVER_LIB)
  message(WARNING
    "Could not find gpu_roce_transceiver library. "
    "gpu_roce_bridge will not be built.")
endif()

# --------------------------------------------------------------------------- #
# Find transitive HSB libraries
# --------------------------------------------------------------------------- #

find_library(HSB_COMMON_LIB
  NAMES hololink
  PATHS
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/src/hololink/common"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/lib"
  NO_DEFAULT_PATH)

find_library(BASE_RECEIVER_OP_LIB
  NAMES base_receiver_op
  PATHS
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/src/hololink/operators"
    "${HOLOSCAN_SENSOR_BRIDGE_BUILD_DIR}/lib"
  NO_DEFAULT_PATH)

find_library(IBVERBS_LIB NAMES ibverbs)

# --------------------------------------------------------------------------- #
# Find DOCA libraries
# --------------------------------------------------------------------------- #

set(DOCA_PATH "/opt/mellanox/doca")

if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64|amd64)")
  set(DOCA_LIB_DIR "${DOCA_PATH}/lib/x86_64-linux-gnu")
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(arm64)")
  set(DOCA_LIB_DIR "${DOCA_PATH}/lib/aarch64-linux-gnu")
else()
  set(DOCA_LIB_DIR "${DOCA_PATH}/lib")
endif()

find_path(DOCA_INCLUDE_DIR doca_verbs.h
  PATHS ${DOCA_PATH}/include
  NO_DEFAULT_PATH)

# RHEL may have DOCA libraries under ${DOCA_PATH}/lib64  
find_library(DOCA_VERBS_LIB doca_verbs
  PATHS ${DOCA_LIB_DIR} ${DOCA_PATH}/lib/ ${DOCA_PATH}/lib64/
  NO_DEFAULT_PATH)

find_library(DOCA_GPUNETIO_LIB doca_gpunetio
  PATHS ${DOCA_LIB_DIR} ${DOCA_PATH}/lib/ ${DOCA_PATH}/lib64/
  NO_DEFAULT_PATH)

find_library(DOCA_COMMON_LIB doca_common
  PATHS ${DOCA_LIB_DIR} ${DOCA_PATH}/lib/ ${DOCA_PATH}/lib64/
  NO_DEFAULT_PATH)

# --------------------------------------------------------------------------- #
# Find Holoscan (required by gpu_roce_transceiver -> holoscan::core)
# --------------------------------------------------------------------------- #

find_package(holoscan QUIET)

# --------------------------------------------------------------------------- #
# Find fmt (transitive dependency of HSB logging)
# --------------------------------------------------------------------------- #

find_path(FMT_INCLUDE_DIR
  NAMES fmt/format.h
  PATHS /opt/nvidia/holoscan /usr/local/cudaq /usr /usr/local
  PATH_SUFFIXES include
  NO_DEFAULT_PATH)

# =========================================================================== #
# hsb_fpga_playback  (uses hololink_core for FPGA control plane)
# =========================================================================== #

add_executable(hsb_fpga_playback
  hsb_fpga_playback.cpp)

target_include_directories(hsb_fpga_playback
  PRIVATE
    ${CUDAQ_REALTIME_INCLUDE_DIR}
    "${HOLOSCAN_SENSOR_BRIDGE_SOURCE_DIR}/src"
    "${CUDAToolkit_INCLUDE_DIRS}")

if (FMT_INCLUDE_DIR)
  target_include_directories(hsb_fpga_playback
    PRIVATE "${FMT_INCLUDE_DIR}")
endif()

target_link_libraries(hsb_fpga_playback
  PRIVATE ${HSB_CORE_LIB} CUDA::cuda_driver Threads::Threads)

# Install this to so it's available for testing the installer
install(TARGETS hsb_fpga_playback
        DESTINATION ${CMAKE_INSTALL_BINDIR}
        COMPONENT hsb-tools)

# =========================================================================== #
# gpu_roce_bridge  (generic increment bridge)
# =========================================================================== #

if (GPU_ROCE_TRANSCEIVER_LIB AND
    DOCA_INCLUDE_DIR AND DOCA_VERBS_LIB AND DOCA_COMMON_LIB AND
    DOCA_GPUNETIO_LIB)

  message(STATUS "Building gpu_roce_bridge (generic increment)")
  message(STATUS "  GPU RoCE Transceiver: ${GPU_ROCE_TRANSCEIVER_LIB}")

  # Increment function table (compiled by nvcc)
  add_library(rpc_increment_ft STATIC
    init_rpc_increment_function_table.cu)

  set_target_properties(rpc_increment_ft PROPERTIES
    CUDA_SEPARABLE_COMPILATION ON
    CUDA_STANDARD 20)

  target_include_directories(rpc_increment_ft PRIVATE
    ${CUDAQ_REALTIME_INCLUDE_DIR}
    ${CUDAToolkit_INCLUDE_DIRS})

  # Bridge executable (.cpp, linked with CUDA)
  add_executable(gpu_roce_bridge
    gpu_roce_bridge.cpp)

  set_target_properties(gpu_roce_bridge PROPERTIES
    LINKER_LANGUAGE CUDA
    CUDA_SEPARABLE_COMPILATION ON
    CUDA_RESOLVE_DEVICE_SYMBOLS ON)

  target_include_directories(gpu_roce_bridge
    PRIVATE
      ${CMAKE_CURRENT_SOURCE_DIR}
      ${CUDAQ_REALTIME_INCLUDE_DIR}
      ${CUDAToolkit_INCLUDE_DIRS})

  # Link order: static archives first, then shared
  target_link_libraries(gpu_roce_bridge
    PRIVATE
      rpc_increment_ft
      cudaq-realtime-dispatch
      cudaq-realtime-bridge-gpu-roce
      ${GPU_ROCE_TRANSCEIVER_LIB}
      ${BASE_RECEIVER_OP_LIB}
      ${HSB_CORE_LIB}
      ${HSB_COMMON_LIB}
      cudaq-realtime
      CUDA::cudart
      CUDA::cuda_driver
      ${DOCA_VERBS_LIB}
      ${DOCA_GPUNETIO_LIB}
      ${DOCA_COMMON_LIB}
      ${IBVERBS_LIB}
      Threads::Threads
      ${CMAKE_DL_LIBS})

  if (holoscan_FOUND)
    target_link_libraries(gpu_roce_bridge PRIVATE holoscan::core)
  endif()

  # Set RPATH for shared libraries
  set_target_properties(gpu_roce_bridge PROPERTIES
    BUILD_RPATH "${DOCA_LIB_DIR}"
    INSTALL_RPATH "${DOCA_LIB_DIR}")

  # Install the bridge executable
  install(TARGETS gpu_roce_bridge
          DESTINATION ${CMAKE_INSTALL_BINDIR}
          COMPONENT hsb-tools)  
else()
  if (NOT GPU_ROCE_TRANSCEIVER_LIB)
    message(WARNING "gpu_roce_transceiver library not found. "
                    "gpu_roce_bridge will not be built.")
  endif()
  if (NOT DOCA_INCLUDE_DIR OR NOT DOCA_VERBS_LIB)
    message(WARNING "DOCA libraries not found. "
                    "gpu_roce_bridge requires DOCA.")
  endif()
endif()

# =========================================================================== #
# hsb_fpga_emulator  (software FPGA, libibverbs only)
# =========================================================================== #

if (IBVERBS_LIB)
  message(STATUS "Building hsb_fpga_emulator")

  add_executable(hsb_fpga_emulator
    hsb_fpga_emulator.cpp)

  target_link_libraries(hsb_fpga_emulator
    PRIVATE
      ${IBVERBS_LIB}
      Threads::Threads)
  # install the emulator binary for testing the installer
  install(TARGETS hsb_fpga_emulator
          DESTINATION ${CMAKE_INSTALL_BINDIR}
          COMPONENT hsb-tools)
else()
  message(WARNING "libibverbs not found. hsb_fpga_emulator will not be built.")
endif()
