#!/bin/sh
# ── configure ──
# This script runs on Linux/macOS (never on Windows).
# It detects RcppParallel, OpenCL headers/libs, performs a non-PoCL GPU runtime probe,
# detects CUDA (optional), and generates src/Makevars dynamically.

# Use R_HOME/bin/Rscript (Writing R Extensions). Never invoke bare "R" or "Rscript"
# (some rhub images print a warning to stdout/stderr that has been mistaken for R_HOME).
# R CMD INSTALL normally sets R_HOME; if it is missing or bogus, derive from PATH.
R_HOME_EFFECTIVE=""
if [ -n "$R_HOME" ] && [ -x "$R_HOME/bin/Rscript" ]; then
  R_HOME_EFFECTIVE="$R_HOME"
else
  R_BIN=$(command -v R 2>/dev/null || true)
  if [ -n "$R_BIN" ] && [ -x "$R_BIN" ]; then
    R_HOME_EFFECTIVE=$(cd "$(dirname "$R_BIN")/.." && pwd)
  fi
  if [ -z "$R_HOME_EFFECTIVE" ] || [ ! -x "$R_HOME_EFFECTIVE/bin/Rscript" ]; then
    RS_BIN=$(command -v Rscript 2>/dev/null || true)
    if [ -n "$RS_BIN" ] && [ -x "$RS_BIN" ]; then
      R_HOME_EFFECTIVE=$(cd "$(dirname "$RS_BIN")/.." && pwd)
    fi
  fi
fi

if [ -z "$R_HOME_EFFECTIVE" ] || [ ! -x "$R_HOME_EFFECTIVE/bin/Rscript" ]; then
  echo "configure: could not resolve R_HOME with executable bin/Rscript (R_HOME=$R_HOME)" >&2
  exit 1
fi
R_HOME="$R_HOME_EFFECTIVE"
RSCRIPT="$R_HOME/bin/Rscript"
echo ">> configure: R_HOME = '$R_HOME'"

# ---- 1. Detect RcppParallel include and library paths ----
RPP_INC_DIR=$("$RSCRIPT" -e "cat(system.file('include', package = 'RcppParallel'))")
RPP_LIB_DIR=$("$RSCRIPT" -e "cat(system.file('lib', package = 'RcppParallel'))")

echo ">> Detected RcppParallel include dir: $RPP_INC_DIR"
echo ">> Detected RcppParallel lib dir: $RPP_LIB_DIR"
echo ">> RPP_INC_DIR      = '$RPP_INC_DIR'"
echo ">> RPP_LIB_DIR      = '$RPP_LIB_DIR'"

# ---- 2. Detect Emscripten or Darwin (macOS) and disable OpenMP if needed ----
if [ "$(basename "$CXX")" = "em++" ] || [ "$(basename "$CXX")" = "emcc" ]; then
  EMSCRIPTEN_BUILD=YES
elif command -v emcc >/dev/null 2>&1; then
  EMSCRIPTEN_BUILD=YES
else
  EMSCRIPTEN_BUILD=NO
fi

echo "🔧 EMSCRIPTEN_BUILD=$EMSCRIPTEN_BUILD"

OS=$(uname -s)
echo ">> OS      = '$OS'"

# Disable OpenMP for Emscripten/macOS; otherwise leave empty (OpenMP removed)
if [ "$(basename "$CXX")" = "em++" ] || [ "$OS" = "Darwin" ]; then
  echo "🚀 Emscripten detected: disabling OpenMP flags"
  OPENMP_CFLAGS=""
  OPENMP_LDFLAGS=""
  EMSCRIPTEN_BUILD=YES
else
  EMSCRIPTEN_BUILD=NO
  OPENMP_CFLAGS=""
  OPENMP_LDFLAGS=""
fi

# ---- 3. Core flags ----
OPENCL_FOUND=no
# Link against the same BLAS/LAPACK as this R (Writing R Extensions). Using
# -llapack/-lblas breaks on minimal Linux images (e.g. rhub) where those
# -l names are not on the default linker path.
R_LAPACK_BLAS_FLIBS='$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)'

# ---- 4. RcppParallel flags ----
RPP_CPPFLAGS="-I$RPP_INC_DIR"
RPP_LDFLAGS="-L$RPP_LIB_DIR -ltbb -ltbbmalloc -Wl,-rpath,${RPP_LIB_DIR}"

echo ">> RPP_CPPFLAGS = '$RPP_CPPFLAGS'"
echo ">> RPP_LDFLAGS = '$RPP_LDFLAGS'"

# Legacy variable (unused but must remain because it was active)
RCPPPARALLEL_FLAGS="-L$RCPPPARALLEL_LIB -ltbb -I$RCPPPARALLEL_INC"

#!/bin/bash

# ---- 5. Collect system include directories ----
echo "📚 Collecting system include directories from gcc..."
SYSTEM_INCLUDE_DIRS=$(echo | gcc -E -x c++ - -v 2>&1 | grep "^ /" | sed 's/^ //')
echo "$SYSTEM_INCLUDE_DIRS"

OPENCL_HEADER="CL/cl.h"
OPENCL_INCLUDE_FOUND=no
OPENCL_LIB_HEADER="libOpenCL.so"
OPENCL_LIB_FOUND=no
OPENCL_FOUND=no
OPENCL_INCLUDE=""
OPENCL_LIB=""

# ---- 6. Search for OpenCL headers ----
for dir in $SYSTEM_INCLUDE_DIRS; do
    echo "🔎 Checking: $dir"
    if [ -f "$dir/$OPENCL_HEADER" ]; then
        OPENCL_INCLUDE="-I$dir"
        OPENCL_INCLUDE_FOUND=yes
        echo "✅ Found OpenCL header in: $dir"
        break
    fi
done

# ---- 7. Collect system library directories ----
echo "📚 Collecting system lib directories from gcc..."
RAW_LIB_DIRS=$(gcc -Xlinker --verbose 2>&1 | grep SEARCH_DIR | sed 's/SEARCH_DIR("=*\([^"]*\)").*/\1/')
echo "$RAW_LIB_DIRS"

ALT_LIB_DIRS=$(echo "$RAW_LIB_DIRS" | sed 's|/usr/local|/usr|g')
echo "$ALT_LIB_DIRS"

SYSTEM_LIB_DIRS=$(echo "$RAW_LIB_DIRS $ALT_LIB_DIRS" | tr ' ' '\n' | sort -u)
echo "$SYSTEM_LIB_DIRS"

# ---- 8. Search for OpenCL libraries ----
for dir in $SYSTEM_LIB_DIRS; do
    echo "🔎 Checking: $dir"
    if ls "$dir"/libOpenCL.so* >/dev/null 2>&1; then
        OPENCL_LIB="-L$dir -lOpenCL"
        OPENCL_LIBDIR="$dir"
        OPENCL_LIB_FOUND=yes
        echo "✅ Found OpenCL library in: $dir"
        break
    fi
done

# ---- 9. Finalize OpenCL_FOUND ----
if [ "$OPENCL_INCLUDE_FOUND" = "yes" ] && [ "$OPENCL_LIB_FOUND" = "yes" ]; then
    OPENCL_FOUND=yes
else
    OPENCL_FOUND=no
fi

# ---- 10. Handle Emscripten-specific OpenCL include logic ----
if [ "$OPENCL_INCLUDE_FOUND" = "yes" ] && [ "$OPENCL_LIB_FOUND" = "yes" ]; then
  OPENCL_FOUND=yes

  if [ "$EMSCRIPTEN_BUILD" = "YES" ]; then
    OPENCL_INCLUDE="${OPENCL_INCLUDE}/CL -I../inst/include"
    echo "🚀 Emscripten build: using direct CL header folder ${OPENCL_INCLUDE}/CL"
  else
    echo "✅ Enabling OpenCL: include directory ${OPENCL_INCLUDE}"
  fi
else
  OPENCL_FOUND=no
  echo "⚠️  OpenCL not found: falling back to CPU-only"
fi

# ---- 11. Runtime OpenCL probe (non-PoCL GPU required for USE_OPENCL) ----
# PoCL-only ICDs on CRAN Linux builders expose a platform but compile kernels into
# ~/.cache/pocl during check (clBuildProgram), causing incoming NOTEs. Enable
# USE_OPENCL only when a non-PoCL platform exposes at least one GPU device.
OPENCL_RUNTIME_FOUND=no
if [ "$OPENCL_FOUND" = "yes" ] && [ "$EMSCRIPTEN_BUILD" != "YES" ]; then
  echo "Checking for a non-PoCL OpenCL GPU runtime..."
  cat > conftest_opencl_gpu.c <<'EOF'
#include <CL/cl.h>
#include <stdlib.h>
#include <string.h>

static int platform_is_pocl(cl_platform_id platform) {
  char name[512];
  char vendor[512];
  if (clGetPlatformInfo(platform, CL_PLATFORM_NAME, sizeof(name), name, NULL) != CL_SUCCESS)
    return 0;
  if (clGetPlatformInfo(platform, CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL) != CL_SUCCESS)
    vendor[0] = '\0';
  if (strstr(name, "PoCL") || strstr(name, "Portable Computing Language"))
    return 1;
  if (strstr(vendor, "PoCL") || strstr(vendor, "Portable Computing Language"))
    return 1;
  return 0;
}

int main(void) {
  cl_uint np = 0;
  if (clGetPlatformIDs(0, NULL, &np) != CL_SUCCESS || np == 0)
    return 1;

  cl_platform_id *platforms = (cl_platform_id *)malloc(np * sizeof(cl_platform_id));
  if (!platforms)
    return 1;
  if (clGetPlatformIDs(np, platforms, NULL) != CL_SUCCESS) {
    free(platforms);
    return 1;
  }

  int found_gpu = 0;
  for (cl_uint i = 0; i < np; i++) {
    if (platform_is_pocl(platforms[i]))
      continue;
    cl_uint nd = 0;
    if (clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_GPU, 0, NULL, &nd) == CL_SUCCESS && nd > 0) {
      found_gpu = 1;
      break;
    }
  }
  free(platforms);
  return found_gpu ? 0 : 1;
}
EOF

  ${CC} conftest_opencl_gpu.c $OPENCL_LIB -o conftest_opencl_gpu >/dev/null 2>&1

  if env LD_LIBRARY_PATH=$OPENCL_LIBDIR:$LD_LIBRARY_PATH ./conftest_opencl_gpu >/dev/null 2>&1; then
    echo "Non-PoCL OpenCL GPU runtime detected."
    OPENCL_RUNTIME_FOUND=yes
  else
    echo "No non-PoCL OpenCL GPU runtime detected (PoCL-only or no GPU platform)."
    OPENCL_RUNTIME_FOUND=no
  fi

  rm -f conftest_opencl_gpu conftest_opencl_gpu.c
fi

echo "Final OpenCL Detection:"
echo "OPENCL_INCLUDE_FOUND = $OPENCL_INCLUDE_FOUND"
echo "OPENCL_LIB_FOUND     = $OPENCL_LIB_FOUND"
echo "OPENCL_FOUND         = $OPENCL_FOUND"
echo "OPENCL_RUNTIME_FOUND = $OPENCL_RUNTIME_FOUND"

# ---- 12. Compose initial flags ----
FULL_CXXFLAGS="$OPENMP_CFLAGS"
FULL_LIBS="$OPENMP_LDFLAGS $R_LAPACK_BLAS_FLIBS"

# ---- 13. Add OpenCL flags if available ----
if [ "$OPENCL_FOUND" = "yes" ]; then
  echo "OpenCL headers/libs discovered"
  FULL_CXXFLAGS="$FULL_CXXFLAGS $OPENCL_INCLUDE"
  FULL_LIBS="$FULL_LIBS $OPENCL_LIB"

  if [ "$OPENCL_RUNTIME_FOUND" = "yes" ]; then
    echo "OpenCL GPU runtime probe succeeded"
    FULL_CXXFLAGS="$FULL_CXXFLAGS -DUSE_OPENCL"
  else
    echo "OpenCL GPU runtime probe failed, not defining USE_OPENCL"
  fi

  if [ "$EMSCRIPTEN_BUILD" = "YES" ]; then
    echo "Emscripten build: injecting direct cl.h include flag"
    FULL_CXXFLAGS="$FULL_CXXFLAGS -DUSE_DIRECT_CLH"
  fi
else
  echo "OpenCL manually disabled or not discovered"
fi

# ---- 14. Detect CUDA (optional) ----
echo "Checking for NVIDIA GPU via nvidia-smi..."

CUDA_LIBDIR=""
for dir in $SYSTEM_LIB_DIRS; do
    if ls "$dir"/libcuda.so* >/dev/null 2>&1; then
        CUDA_LIBDIR="$dir"
        echo "✔ Found CUDA driver library in: $dir"
        break
    fi
done

GPU_NAME=$(env LD_LIBRARY_PATH=$CUDA_LIBDIR:$LD_LIBRARY_PATH \
           /usr/bin/nvidia-smi --query-gpu=name --format=csv,noheader 2>&1)

if [ -z "$CUDA_LIBDIR" ]; then
    echo "⚠️ CUDA driver library not found — GPU detection may fail."
fi

if [ -n "$GPU_NAME" ]; then
    echo "GPU detected: $GPU_NAME"
    GPU_FOUND=yes
else
    echo "No GPU detected (nvidia-smi returned empty or error)."
    GPU_FOUND=no
fi

echo ">> FULL_CXXFLAGS    = '$FULL_CXXFLAGS'"
echo ">> FULL_LIBS        = '$FULL_LIBS'"

# ---- 15. Append RcppParallel flags ----
FULL_LIBS="$FULL_LIBS $RPP_LDFLAGS"
FULL_CXXFLAGS="$FULL_CXXFLAGS $RPP_CPPFLAGS"

echo ">> FULL_CXXFLAGS    = '$FULL_CXXFLAGS'"
echo ">> FULL_LIBS        = '$FULL_LIBS'"

# ---- 16. Write final Makevars ----
echo "PKG_CXXFLAGS = $FULL_CXXFLAGS" > src/Makevars
echo "PKG_LIBS = $FULL_LIBS" >> src/Makevars
