# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Build the cuDSS standard-Fortran examples.
#
# The Fortran interface (cudss.f90, cudss_mpi.f90, cudss_fortran_mpi.c) ships under
# include/fortran/. By default it is compiled from $(CUDSS_INTERFACE_DIR); set
# CUDSS_INTERFACE_BUILD_DIR to reuse an interface you built elsewhere.

# Standard-Fortran examples: default to the generic Fortran compiler; set FC to
# your toolchain (e.g. gfortran, ifx, or nvfortran).
FC        ?= f77
MPIF90    ?= mpif90
FFLAGS    ?= -O2 -fPIC
MPICC     ?= mpicc
CFLAGS    ?= -O2 -fPIC
CUDSS_INC ?= cudss_inc
CUDSS_LIB ?= cudss_lib
CUDA_INC  ?= cuda_inc
CUDA_LIB  ?= cuda_lib

# cuDSS Fortran interface sources (shipped under include/fortran/, sibling of examples/).
CUDSS_INTERFACE_DIR ?= ../../include/fortran
# Point this at a directory holding a prebuilt interface (cudss.mod/cudss.o,
# cudss_mpi.mod/.o, cudss_fortran_mpi.o) to reuse it instead of recompiling. Empty ->
# build the interface from source in $(CUDSS_INTERFACE_DIR).
CUDSS_INTERFACE_BUILD_DIR ?=

SRCDIR   := .
BINDIR   := bin
MODDIR   := .
$(shell mkdir -p $(BINDIR))


FC_BASE := $(notdir $(FC))
ifneq ($(filter nvfortran ifx ifort mpiifx mpiifort,$(FC_BASE)),)
MODFLAG  := -module $(MODDIR)
else
MODFLAG  := -J$(MODDIR)
endif

ifeq ($(filter nvfortran,$(FC_BASE)),$(FC_BASE))
LIBS     := $(CUDSS_LIB)/libcudss.so -cudalib=cublas -Wl,-rpath,$(CUDSS_LIB)
else
LIBS     := -L$(CUDSS_LIB) -lcudss -L$(CUDA_LIB) -lcudart -lcublas
LDFLAGS  += -Wl,-rpath,$(CUDSS_LIB) -Wl,-rpath,$(CUDA_LIB)
endif

INC     := -I$(MODDIR)
FFLAGS  += $(INC)

CUDSS_SRC       := $(CUDSS_INTERFACE_DIR)/cudss.f90
CUDSS_MOD       := $(MODDIR)/cudss.mod
CUDSS_FORTRAN_MPI_SRC := $(CUDSS_INTERFACE_DIR)/cudss_fortran_mpi.c
CUDSS_MPI_SRC   := $(CUDSS_INTERFACE_DIR)/cudss_mpi.f90
CUDSS_MPI_MOD   := $(MODDIR)/cudss_mpi.mod

# Interface objects: built locally by default, or taken from a prebuilt dir.
ifeq ($(strip $(CUDSS_INTERFACE_BUILD_DIR)),)
CUDSS_OBJ       := cudss.o
CUDSS_FORTRAN_MPI_OBJ := cudss_fortran_mpi.o
CUDSS_MPI_OBJ   := cudss_mpi.o
else
CUDSS_OBJ       := $(CUDSS_INTERFACE_BUILD_DIR)/cudss.o
CUDSS_FORTRAN_MPI_OBJ := $(CUDSS_INTERFACE_BUILD_DIR)/cudss_fortran_mpi.o
CUDSS_MPI_OBJ   := $(CUDSS_INTERFACE_BUILD_DIR)/cudss_mpi.o
INC += -I$(CUDSS_INTERFACE_BUILD_DIR)
endif

UTILS_OBJS := cudautils.o cudssutils.o validation.o
UTILS_MODS := $(MODDIR)/cudautils.mod $(MODDIR)/cudssutils.mod $(MODDIR)/validation.mod

EXAMPLES := \
	cudss_batch \
	cudss_batch_sparse_matrix_helpers \
	cudss_dense_matrix_helpers \
	cudss_get_set \
	cudss_high_precision \
	cudss_hybrid_execute_mode \
	cudss_hybrid_memory_mode \
	cudss_memory_handler \
	cudss_mg_mode \
	cudss_mgmn_mode \
	cudss_multithreaded_mode \
	cudss_reordering_phase \
	cudss_residual \
	cudss_schur_complement \
	cudss_simple \
	cudss_simple_complex \
	cudss_sparse_matrix_helpers \
	cudss_uniform_batch

EXAMPLE_BINS := $(addprefix $(BINDIR)/,$(EXAMPLES))

CUDSS_DEPS   := $(CUDSS_OBJ)
UTILS_DEPS   := $(UTILS_OBJS)
LINK_FULL    := $(UTILS_OBJS) $(CUDSS_OBJ)
LINK_MIN     := cudautils.o $(CUDSS_OBJ)
LINK_MIN_VAL := cudautils.o validation.o $(CUDSS_OBJ)


MPIF90_DRIVER := $(notdir $(firstword $(shell $(MPIF90) -show 2>/dev/null)))
MPIF90_BASE := $(if $(MPIF90_DRIVER),$(MPIF90_DRIVER),$(notdir $(MPIF90)))
ifneq ($(filter nvfortran ifx ifort mpiifx mpiifort,$(MPIF90_BASE)),)
MPI_MODFLAG := -module $(MODDIR)
else
MPI_MODFLAG := -J$(MODDIR)
endif
MPIRUN              ?= mpirun
MPI_NP              ?= 2
CUDSS_COMM_LAYER    ?= $(CUDSS_LIB)/libcudss_commlayer_openmpi.so
CUDSS_THREADING_LAYER ?= $(CUDSS_LIB)/libcudss_mtlayer_gomp.so

.PHONY: help usage build cudss utils cudss_mpi examples run clean $(EXAMPLES)

help: usage

usage:
	@printf '%s\n' \
	  'Usage: make [target] [VAR=value ...]' \
	  '' \
	  'Targets:' \
	  '  build       Build cuDSS modules, utilities, MPI support, and all examples' \
	  '  cudss       Build the core cuDSS Fortran module' \
	  '  cudss_mpi   Build the MPI support module and C wrapper' \
	  '  utils       Build utility modules' \
	  '  examples    Build all example binaries' \
	  '  run         Build examples and run them, writing outputs under $(BINDIR)' \
	  '  clean       Remove generated objects, modules, and example binaries' \
	  '  usage/help  Print this usage information' \
	  '' \
	  'Example targets:' \
	  '  $(EXAMPLES)' \
	  '' \
	  'Key variables:' \
	  '  FC=$(FC)' \
	  '  FFLAGS=$(FFLAGS)' \
	  '  MPIF90=$(MPIF90)' \
	  '  MPICC=$(MPICC)' \
	  '  CFLAGS=$(CFLAGS)' \
	  '  CUDSS_INC=$(CUDSS_INC)' \
	  '  CUDSS_LIB=$(CUDSS_LIB)' \
	  '  CUDA_INC=$(CUDA_INC)' \
	  '  CUDA_LIB=$(CUDA_LIB)' \
	  '  LDFLAGS=$(LDFLAGS)' \
	  '  MPIRUN=$(MPIRUN)' \
	  '  MPI_NP=$(MPI_NP)' \
	  '  CUDSS_COMM_LAYER=$(CUDSS_COMM_LAYER)' \
	  '  CUDSS_THREADING_LAYER=$(CUDSS_THREADING_LAYER)'

build: cudss cudss_mpi utils examples

examples: $(EXAMPLE_BINS)

cudss: $(CUDSS_OBJ)

ifeq ($(strip $(CUDSS_INTERFACE_BUILD_DIR)),)
$(CUDSS_OBJ): $(CUDSS_SRC)
	$(FC) $(FFLAGS) $(MODFLAG) -c $<
endif

utils: $(UTILS_OBJS)

cudss_mpi: $(CUDSS_MPI_OBJ) $(CUDSS_FORTRAN_MPI_OBJ)

cudautils.o: cudautils.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c cudautils.f90 -o cudautils.o

cudssutils.o: cudssutils.f90 $(CUDSS_OBJ) cudautils.o
	$(FC) $(FFLAGS) $(MODFLAG) -c cudssutils.f90 -o cudssutils.o

validation.o: validation.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c validation.f90 -o validation.o

ifeq ($(strip $(CUDSS_INTERFACE_BUILD_DIR)),)
$(CUDSS_FORTRAN_MPI_OBJ): $(CUDSS_FORTRAN_MPI_SRC)
	$(MPICC) $(CFLAGS) -I$(CUDSS_INC) -I$(CUDA_INC) -c $< -o $@
endif

ifeq ($(strip $(CUDSS_INTERFACE_BUILD_DIR)),)
$(CUDSS_MPI_OBJ): $(CUDSS_MPI_SRC) $(CUDSS_OBJ)
	$(FC) $(FFLAGS) $(MODFLAG) -c $(CUDSS_MPI_SRC) -o $(CUDSS_MPI_OBJ)
endif

cudss_batch: $(BINDIR)/cudss_batch

$(BINDIR)/cudss_batch.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_batch.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_batch.f90 -o $@

$(BINDIR)/cudss_batch: $(BINDIR)/cudss_batch.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_batch.o $(LINK_FULL) $(LIBS)

cudss_batch_sparse_matrix_helpers: $(BINDIR)/cudss_batch_sparse_matrix_helpers

$(BINDIR)/cudss_batch_sparse_matrix_helpers.o: $(CUDSS_DEPS) cudautils.o $(SRCDIR)/cudss_batch_sparse_matrix_helpers.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_batch_sparse_matrix_helpers.f90 -o $@

$(BINDIR)/cudss_batch_sparse_matrix_helpers: $(BINDIR)/cudss_batch_sparse_matrix_helpers.o $(LINK_MIN)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_batch_sparse_matrix_helpers.o $(LINK_MIN) $(LIBS)

cudss_dense_matrix_helpers: $(BINDIR)/cudss_dense_matrix_helpers

$(BINDIR)/cudss_dense_matrix_helpers.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_dense_matrix_helpers.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_dense_matrix_helpers.f90 -o $@

$(BINDIR)/cudss_dense_matrix_helpers: $(BINDIR)/cudss_dense_matrix_helpers.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_dense_matrix_helpers.o $(LINK_FULL) $(LIBS)

cudss_get_set: $(BINDIR)/cudss_get_set

$(BINDIR)/cudss_get_set.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_get_set.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_get_set.f90 -o $@

$(BINDIR)/cudss_get_set: $(BINDIR)/cudss_get_set.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_get_set.o $(LINK_FULL) $(LIBS)

cudss_high_precision: $(BINDIR)/cudss_high_precision

$(BINDIR)/cudss_high_precision.o: $(CUDSS_DEPS) cudautils.o $(SRCDIR)/cudss_high_precision.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_high_precision.f90 -o $@

$(BINDIR)/cudss_high_precision: $(BINDIR)/cudss_high_precision.o $(LINK_MIN)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_high_precision.o $(LINK_MIN) $(LIBS)

cudss_hybrid_execute_mode: $(BINDIR)/cudss_hybrid_execute_mode

$(BINDIR)/cudss_hybrid_execute_mode.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_hybrid_execute_mode.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_hybrid_execute_mode.f90 -o $@

$(BINDIR)/cudss_hybrid_execute_mode: $(BINDIR)/cudss_hybrid_execute_mode.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_hybrid_execute_mode.o $(LINK_FULL) $(LIBS)

cudss_hybrid_memory_mode: $(BINDIR)/cudss_hybrid_memory_mode

$(BINDIR)/cudss_hybrid_memory_mode.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_hybrid_memory_mode.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_hybrid_memory_mode.f90 -o $@

$(BINDIR)/cudss_hybrid_memory_mode: $(BINDIR)/cudss_hybrid_memory_mode.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_hybrid_memory_mode.o $(LINK_FULL) $(LIBS)

cudss_memory_handler: $(BINDIR)/cudss_memory_handler

$(BINDIR)/cudss_memory_handler.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_memory_handler.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_memory_handler.f90 -o $@

$(BINDIR)/cudss_memory_handler: $(BINDIR)/cudss_memory_handler.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_memory_handler.o $(LINK_FULL) $(LIBS)

cudss_mg_mode: $(BINDIR)/cudss_mg_mode

$(BINDIR)/cudss_mg_mode.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_mg_mode.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_mg_mode.f90 -o $@

$(BINDIR)/cudss_mg_mode: $(BINDIR)/cudss_mg_mode.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_mg_mode.o $(LINK_FULL) $(LIBS)

cudss_mgmn_mode: $(BINDIR)/cudss_mgmn_mode

$(BINDIR)/cudss_mgmn_mode.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(CUDSS_MPI_OBJ) $(SRCDIR)/cudss_mgmn_mode.f90
	$(MPIF90) $(FFLAGS) $(MPI_MODFLAG) -c $(SRCDIR)/cudss_mgmn_mode.f90 -o $@

$(BINDIR)/cudss_mgmn_mode: $(BINDIR)/cudss_mgmn_mode.o $(LINK_FULL) $(CUDSS_MPI_OBJ) $(CUDSS_FORTRAN_MPI_OBJ)
	$(MPIF90) $(FFLAGS) $(LDFLAGS) -o $@ \
		$(BINDIR)/cudss_mgmn_mode.o $(LINK_FULL) $(CUDSS_MPI_OBJ) $(CUDSS_FORTRAN_MPI_OBJ) $(LIBS)

cudss_multithreaded_mode: $(BINDIR)/cudss_multithreaded_mode

$(BINDIR)/cudss_multithreaded_mode.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_multithreaded_mode.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_multithreaded_mode.f90 -o $@

$(BINDIR)/cudss_multithreaded_mode: $(BINDIR)/cudss_multithreaded_mode.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_multithreaded_mode.o $(LINK_FULL) $(LIBS)

cudss_reordering_phase: $(BINDIR)/cudss_reordering_phase

$(BINDIR)/cudss_reordering_phase.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_reordering_phase.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_reordering_phase.f90 -o $@

$(BINDIR)/cudss_reordering_phase: $(BINDIR)/cudss_reordering_phase.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_reordering_phase.o $(LINK_FULL) $(LIBS)

cudss_residual: $(BINDIR)/cudss_residual

$(BINDIR)/cudss_residual.o: $(CUDSS_DEPS) cudautils.o validation.o $(SRCDIR)/cudss_residual.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_residual.f90 -o $@

$(BINDIR)/cudss_residual: $(BINDIR)/cudss_residual.o $(LINK_MIN_VAL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_residual.o $(LINK_MIN_VAL) $(LIBS)

cudss_schur_complement: $(BINDIR)/cudss_schur_complement

$(BINDIR)/cudss_schur_complement.o: $(CUDSS_DEPS) cudautils.o $(SRCDIR)/cudss_schur_complement.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_schur_complement.f90 -o $@

$(BINDIR)/cudss_schur_complement: $(BINDIR)/cudss_schur_complement.o $(LINK_MIN)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_schur_complement.o $(LINK_MIN) $(LIBS)

cudss_simple: $(BINDIR)/cudss_simple

$(BINDIR)/cudss_simple.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_simple.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_simple.f90 -o $@

$(BINDIR)/cudss_simple: $(BINDIR)/cudss_simple.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_simple.o $(LINK_FULL) $(LIBS)

cudss_simple_complex: $(BINDIR)/cudss_simple_complex

$(BINDIR)/cudss_simple_complex.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_simple_complex.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_simple_complex.f90 -o $@

$(BINDIR)/cudss_simple_complex: $(BINDIR)/cudss_simple_complex.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_simple_complex.o $(LINK_FULL) $(LIBS)

cudss_sparse_matrix_helpers: $(BINDIR)/cudss_sparse_matrix_helpers

$(BINDIR)/cudss_sparse_matrix_helpers.o: $(CUDSS_DEPS) cudautils.o $(SRCDIR)/cudss_sparse_matrix_helpers.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_sparse_matrix_helpers.f90 -o $@

$(BINDIR)/cudss_sparse_matrix_helpers: $(BINDIR)/cudss_sparse_matrix_helpers.o $(LINK_MIN)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_sparse_matrix_helpers.o $(LINK_MIN) $(LIBS)

cudss_uniform_batch: $(BINDIR)/cudss_uniform_batch

$(BINDIR)/cudss_uniform_batch.o: $(CUDSS_DEPS) $(UTILS_DEPS) $(SRCDIR)/cudss_uniform_batch.f90
	$(FC) $(FFLAGS) $(MODFLAG) -c $(SRCDIR)/cudss_uniform_batch.f90 -o $@

$(BINDIR)/cudss_uniform_batch: $(BINDIR)/cudss_uniform_batch.o $(LINK_FULL)
	$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $(BINDIR)/cudss_uniform_batch.o $(LINK_FULL) $(LIBS)

run: examples
	@set -e; \
	for ex in $(EXAMPLES); do \
	  if [ "$$ex" = "cudss_mgmn_mode" ]; then \
	    $(MPIRUN) -np $(MPI_NP) $(BINDIR)/$$ex $(CUDSS_COMM_LAYER) > $(BINDIR)/$$ex.out 2>&1; \
	  elif [ "$$ex" = "cudss_multithreaded_mode" ]; then \
	    $(BINDIR)/$$ex $(CUDSS_THREADING_LAYER) > $(BINDIR)/$$ex.out 2>&1; \
	  else \
	    $(BINDIR)/$$ex > $(BINDIR)/$$ex.out 2>&1; \
	  fi; \
	  echo "Test $$ex PASSED"; \
	done

clean:
	$(RM) $(CUDSS_OBJ) $(CUDSS_MOD) $(CUDSS_FORTRAN_MPI_OBJ) $(CUDSS_MPI_OBJ) $(CUDSS_MPI_MOD)
	$(RM) $(UTILS_OBJS) $(UTILS_MODS)
	$(RM) $(addprefix $(BINDIR)/,$(addsuffix .o,$(EXAMPLES))) $(EXAMPLE_BINS)
	$(RM) $(MODDIR)/memhandler.mod $(MODDIR)/matrices.mod
	$(RM) $(BINDIR)/*.out
	$(RM) $(BINDIR)/*.mod
