#################################################################
#  Makefile for Monte Carlo eXtreme (MCX)
#  Qianqian Fang <q.fang at neu.edu>
#  2009/04/02
#################################################################

BACKEND ?= cuda

ROOTDIR ?= ..
MCXDIR  ?= $(ROOTDIR)
MCXSRC  :=$(MCXDIR)/src

ifeq ($(BACKEND),ocelot)
  CC?=g++
endif

MKDIR      := mkdir

CUDACC?=nvcc
MEX=mex
AR=$(CXX)

GCC?=gcc

BINARY=mcx
MAKE=make
OUTPUT_DIR=$(MCXDIR)/bin

DOXY       := doxygen
DOCDIR     := $(MCXDIR)/doc
DOXYCFG=mcx_doxygen.cfg
ZMATLIB    :=lib/libzmat.a
# zmat deflates and inflates zlib/gzip in parallel with pthreads (Win32 threads
# with MSVC, the Windows build below)
ZLIBFLAG   :=-lpthread
USERLINKOPT?=$(ZMATLIB) $(ZLIBFLAG)

CUDART?=-lcudart_static -ldl

CUOMPLINK=

ARCH = $(shell uname -m)
PLATFORM = $(shell uname -s)
CUDA_HOME ?= /usr/local/cuda
ISCLANG = $(shell $(CC) --version | grep clang)

INCLUDEDIRS+=${INCLUDES} -Izmat -Izmat/easylzma -I$(CUDA_HOME)/include -Iubj

ifneq ($(findstring Darwin,$(PLATFORM)), Darwin)
  CUDART+=-lrt
endif

ifeq ($(BACKEND),ocelot)
  LINKOPT=-L/usr/local/lib `OcelotConfig -l` -ltinfo
  CUCCOPT+=-D__STRICT_ANSI__ -g #--maxrregcount 32
else ifeq ($(BACKEND),cudastatic)
  ifeq ($(findstring Darwin,$(PLATFORM)), Darwin)
      ifeq ($(ISCLANG),)
          CUDART=-lcudadevrt -lcudart_static -ldl -static-libgcc -static-libstdc++
      else
          CUDART=-lcudadevrt -lcudart_static -ldl /usr/local/lib/libomp.a
      endif
  else
      CUDART=-lcudadevrt -lcudart_static -ldl -lrt -static-libgcc -static-libstdc++
  endif
  LINKOPT=-L$(CUDA_HOME)/lib -lm $(CUDART)
  CUCCOPT+=-g -lineinfo -Xcompiler -Wall#-arch compute_20 #--maxrregcount 32
else
  LINKOPT=-L$(CUDA_HOME)/lib -lm $(CUDART)
  CUCCOPT+=-g -lineinfo -Xcompiler -Wall#-arch compute_20 #--maxrregcount 32
endif

DLLFLAG=-fPIC
OMP=-fopenmp
NVCCOMP=$(OMP)
CUDA_STATIC=--cudart static -Xcompiler "-static-libgcc -static-libstdc++"

CFLAGS+=-std=c99
CPPFLAGS+=-g -Wall #-pedantic #-DNO_LZMA # -DUSE_OS_TIMER

OBJSUFFIX=.o
EXESUFFIX=

FILES=mcx_core mcx_utils mcx_shapes mcx_tictoc mcx mcx_bench mcx_neurojson mcx_mie mcx_lang cjson/cJSON ubj/ubjw

ifeq ($(findstring _NT-,$(PLATFORM)), _NT-)
  CC=nvcc
  CXX=nvcc
  CUOMPLINK=-Xcompiler
  CUDART=-lcudart_static
  ifeq ($(findstring x86_64,$(ARCH)), x86_64)
      LINKOPT=-L"$(CUDA_PATH)/lib/x64" $(CUDART)
  else
      LINKOPT=-L"$(CUDA_PATH)/lib/Win32" $(CUDART)
  endif
  INCLUDEDIRS +=-I"$(CUDA_PATH)/include"
  CPPOPT =-c -D_CRT_SECURE_NO_DEPRECATE -DWIN32 #-DNO_LZMA
  OBJSUFFIX=.obj
  EXESUFFIX=.exe
  DLLFLAG=
  OMP=-openmp
  ifeq ($(findstring CYGWIN,$(PLATFORM)), CYGWIN)
      MEX=cmd /c mex COMPFLAGS='-MT'
  else
      MEX=cmd //s //c mex -v COMPFLAGS='-MT'
  endif
  NVCCOMP=$(CUOMPLINK) "$(OMP)"
  CUCCOPT+=$(NVCCOMP)
  CFLAGS=
  CPPFLAGS=-g
  CUDA_STATIC=--cudart static $(NVCCOMP) -Xcompiler "-MT"
  CPPOPT+=$(NVCCOMP)
  ZLIBFLAG:=
  ZMATLIB    :=lib/libzmat.a
  USERLINKOPT:=${LIBRARIES} $(ZMATLIB) $(ZLIBFLAG)
  MEXLINKLIBS="\$$LINKLIBS"
  export CC CXX
else ifeq ($(findstring Darwin,$(PLATFORM)), Darwin)
  CUDA_STATIC=--cudart static
  OMP=-Xclang -fopenmp
  CPPOPT+=$(OMP)
  LINKOPT+=/usr/local/lib/libomp.a
  INCLUDEDIRS+=-I/usr/local/include
else
  CPPOPT+=$(OMP)
  CUCCOPT+=-Xcompiler $(OMP)
  ifeq ($(findstring x86_64,$(ARCH)), x86_64)
     CPPOPT +=-m64
     CUCCOPT +=-m64
     ifeq "$(wildcard $(CUDA_HOME)/lib64)" "$(CUDA_HOME)/lib64"
        LINKOPT=-L$(CUDA_HOME)/lib64 $(CUDART)
     endif
  endif
endif

ifneq ($(findstring _NT-,$(PLATFORM)), _NT-)
  MEXLINKLIBS:=-L"\$$TMW_ROOT\$$MATLABROOT/extern/lib/\$$ARCH" -L"\$$TMW_ROOT\$$MATLABROOT/bin/\$$ARCH" -lmx -lmex $(LINKOPT)
endif


ifneq (,$(filter mex,$(MAKECMDGOALS)))
  FILES=mcx_core mcx_utils mcx_shapes mcx_tictoc mcx_bench mcx_mie mcx_lang cjson/cJSON
  ZMATLIB=
  USERLINKOPT=
  ZLIBFLAG=
endif

ifneq (,$(filter oct,$(MAKECMDGOALS)))
  FILES=mcx_core mcx_utils mcx_shapes mcx_tictoc mcx_bench mcx_mie mcx_lang cjson/cJSON
  ZMATLIB=
  USERLINKOPT=
  ZLIBFLAG=
endif

GPUARCH := $(shell $(CUDACC) --list-gpu-code  2> /dev/null | grep '^sm_' | head -1)
ifeq ($(GPUARCH),)
    CUGENCODE?=-arch=sm_35
else
    CUGENCODE?=-arch=$(GPUARCH)
endif

OUTPUTFLAG:=-o

# the output is accumulated as exact 64-bit fixed-point integers, whose sums do not depend on the
# order of the additions; "make floatsum" builds the float sums of the releases before v2026
ACCUMOPT ?= -DUSE_FIXEDPOINT

##  Target section  ##

kepler: fermi
keplermex: fermimex
kepleroct: fermioct
kepler keplermex kepleroct: CUGENCODE:=-arch=sm_30

maxwell: fermi
maxwellmex: fermimex
maxwelloct: fermioct
maxwell maxwellmex maxwelloct: CUGENCODE:=-arch=sm_50

pascal: fermi
pascalmex: fermimex
pascaloct: fermioct
pascal pascalmex pascaloct: CUGENCODE:=-arch=sm_60

volta: fermi
voltamex: fermimex
voltaoct: fermioct
volta voltamex voltaoct: CUGENCODE:=-arch=sm_70

turing: fermi
turingmex: fermimex
turingoct: fermioct
turing turingmex turingoct: CUGENCODE:=-arch=sm_75

ampere: fermi
amperemex: fermimex
ampereoct: fermioct
ampere amperemex ampereoct: CUGENCODE:=-arch=sm_80

xor xoro posix:  CUCCOPT+=-use_fast_math -DSAVE_DETECTORS
xor:        LINKOPT+=
xoro:       CUCCOPT+=-DUSE_XOROSHIRO128P_RAND
xoro:       LINKOPT+=-DUSE_XOROSHIRO128P_RAND
posix:      CUCCOPT+=-DUSE_POSIX_RAND
posix:      LINKOPT+=-DUSE_POSIX_RAND
fast:       CUCCOPT+=-DUSE_XORSHIFT128P_RAND -use_fast_math
fixed:      fermi
floatsum:   fermi
floatsum:   ACCUMOPT=
debugxor:   xor
fermi fermimex fermioct:      xor
fermimex fermioct: OUTPUTFLAG:=-output

posix xoro fermi:      LINKOPT+=$(CUOMPLINK) "$(OMP)"

debugxor:	CUCCOPT+=-DMCX_DEBUG
fermi fermimex fermioct:     CUCCOPT+=-DSAVE_DETECTORS -use_fast_math $(CUGENCODE) $(ACCUMOPT)
fermi fermimex fermioct: CUCCOPT+=-DMCX_TARGET_NAME='"Lucky Lux"'
xorfermi:   xor
xorofermi:  xoro
posixfermi: posix
xorfermi xorofermi posixfermi debugxor:   LINKOPT+=$(OMP)
xorfermi:    fermi
xorfermi:     CUCCOPT+=-DUSE_XORSHIFT128P_RAND

half: fermi
half: CUCCOPT+=-DUSE_HALF
half: ACCUMOPT=
half: CUGENCODE=-arch=sm_60

double: fermi
double: CUCCOPT+=-DUSE_DOUBLE
double: ACCUMOPT=
double: CUGENCODE=-arch=sm_60

moredouble: fermi
moredouble: CUCCOPT+=-DUSE_MORE_DOUBLE
moredouble: ACCUMOPT=
moredouble: CUGENCODE=-arch=sm_60

static:     fermi
static:     AR=nvcc
static:     CUOMPLINK=-Xcompiler
static:     LINKOPT=$(CUDA_STATIC)

fermimex:   AR=$(MEX)
fermimex:   LINKOPT+= CXXFLAGS='$$CXXFLAGS -g -DSAVE_DETECTORS -DMCX_CONTAINER -DMATLAB_MEX_FILE $(OMP) $(MEXCCOPT) $(USERCCOPT)' LINKLIBS="$(MEXLINKLIBS) $(MEXLINKOPT)" CXXLIBS='$$CXXLIBS $(MEXLINKOPT)' LDFLAGS='-L"$$MATLABROOT/sys/os/$$ARCH" $$LDFLAGS $(OMP) $(USERLINKOPT)'
oct fermimex fermioct:    OUTPUT_DIR=../mcxlab
fermimex:   BINARY=mcx
fermioct:   BINARY=mcx.mex
fermimex fermioct:    CUCCOPT+=--compiler-options "$(DLLFLAG)" -DMCX_CONTAINER -DMATLAB_MEX_FILE
fermimex fermioct:    CPPOPT+=$(DLLFLAG) -DMCX_CONTAINER -DMATLAB_MEX_FILE
fermimex:   LINKOPT+=mcxlab.cpp -outdir $(OUTPUT_DIR) $(INCLUDEDIRS)

fermioct:   AR= CXXFLAGS='-std=c++11 -DSAVE_DETECTORS $(NVCCOMP) $(MEXCCOPT) $(USERCCOPT)' CC='$(CC)' CXX='$(CXX)' LDFLAGS='$(LFLAGS) $(NVCCOMP) $(USERLINKOPT) $(MEXLINKOPT)' XTRA_CFLAGS='$(NVCCOMP)' XTRA_CXXFLAGS='$(NVCCOMP)'  mkoctfile -v
fermioct:   BINARY=mcx.mex
fermioct:   LINKOPT+=--mex mcxlab.cpp $(INCLUDEDIRS)

fermidebug: CUCCOPT+=-DMCX_DEBUG
fermidebug: fermi

register: fermi
register: CUCCOPT+=-Xptxas -O3,-v

all: fermi
debug: debugxor
mex: fermimex
oct: fermioct

##  Command section  ##

doc: makedocdir
	$(DOXY) $(DOXYCFG)

# host-only unit tests of the ray-tracing helpers in mcx_core.cu, no GPU is needed to run
OBJS      := $(addsuffix $(OBJSUFFIX), $(FILES))

UNITTEST_OBJS=$(filter-out mcx_core$(OBJSUFFIX) mcx$(OBJSUFFIX),$(OBJS))

unittest: makedirs $(ZMATLIB) $(UNITTEST_OBJS)
	$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDEDIRS) $(CPPOPT) -I. -c -o test_utils$(OBJSUFFIX) ../test/unit/test_utils.c
	$(CUDACC) -c $(CUCCOPT) -DSAVE_DETECTORS -DMCX_UNITTEST -Xcudafe --diag_suppress=550 $(CUGENCODE) -o test_raytrace$(OBJSUFFIX) ../test/unit/test_raytrace.cu
	$(CUDACC) $(CUCCOPT) $(CUGENCODE) -o $(OUTPUT_DIR)/mcx_unittest$(EXESUFFIX) test_raytrace$(OBJSUFFIX) \
	    test_utils$(OBJSUFFIX) $(UNITTEST_OBJS) $(ZMATLIB) -Xcompiler "$(OMP)" $(ZLIBFLAG)
	$(OUTPUT_DIR)/mcx_unittest$(EXESUFFIX)

TARGETSUFFIX:=$(suffix $(BINARY))

ifeq ($(TARGETSUFFIX),.so)
        CPPOPT+= $(DLLFLAG) 
	CUCCOPT+= -Xcompiler $(DLLFLAG) 
        LINKOPT+= -shared -Wl,-soname,$(BINARY).1 
endif

ifeq ($(TARGETSUFFIX),.a)
        CCFLAGS+=
        AR         := ar  
        ARFLAGS    := cr
        AROUTPUT   :=
        EXTRALIB   :=
        OPENMPLIB  :=
endif

all xor xoro posix fast debugxor half xorfermi xorofermi posixfermi\
 fermi mex oct fermimex fermioct: cudasdk $(OUTPUT_DIR)/$(BINARY)

makedirs:
	@if test ! -d $(OUTPUT_DIR); then $(MKDIR) $(OUTPUT_DIR); fi

makedocdir:
	@if test ! -d $(DOCDIR); then $(MKDIR) $(DOCDIR); fi

$(OUTPUT_DIR)/$(BINARY): makedirs $(ZMATLIB) $(OBJS)
$(OUTPUT_DIR)/$(BINARY): $(OBJS)
	$(AR) $(OBJS) $(OUTPUTFLAG) $(OUTPUT_DIR)/$(BINARY) $(LINKOPT) $(USERLINKOPT)

%$(OBJSUFFIX): %.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDEDIRS) $(CPPOPT) -c -o $@  $<

%$(OBJSUFFIX): %.cpp
	$(CXX) $(CPPFLAGS) $(INCLUDEDIRS) $(CPPOPT) -c -o $@  $<

mcx_bench$(OBJSUFFIX): mcx_bench.c
	$(GCC) $(CFLAGS) $(CPPFLAGS) $(INCLUDEDIRS) -c -o mcx_bench$(OBJSUFFIX)  $<

%$(OBJSUFFIX): %.cu
	$(CUDACC) -c $(CUCCOPT) -o $@  $<

# rebuild the bundled zmat when its sources change, or a stale libzmat.a lingers
$(ZMATLIB): zmat/zmatlib.c zmat/zmatlib.h
	-$(MAKE) -C zmat lib AR=ar CPPOPT="$(DLLFLAG) -O3" USERLINKOPT=
clean:
	-$(MAKE) -C zmat clean
	-rm -f $(OBJS) $(OUTPUT_DIR)/$(BINARY)$(EXESUFFIX) $(OUTPUT_DIR)/$(BINARY)_atomic$(EXESUFFIX) $(OUTPUT_DIR)/$(BINARY)_det$(EXESUFFIX) $(ZMATLIB)
	-rm -f test_utils$(OBJSUFFIX) test_raytrace$(OBJSUFFIX) $(OUTPUT_DIR)/mcx_unittest$(EXESUFFIX)
cudasdk:
	@if [ -z `which ${CUDACC}` ]; then \
	   echo "Please first install CUDA SDK and add the path to nvcc to your PATH environment variable."; exit 1;\
	fi

# derived the astyle settings from https://github.com/nlohmann/json
pretty:
	astyle \
	    --style=attach \
	    --indent=spaces=4 \
	    --indent-modifiers \
	    --indent-switches \
	    --indent-preproc-block \
	    --indent-preproc-define \
	    --indent-col1-comments \
	    --pad-oper \
	    --pad-header \
	    --align-pointer=type \
	    --align-reference=type \
	    --add-brackets \
	    --convert-tabs \
	    --close-templates \
	    --lineend=linux \
	    --preserve-date \
	    --suffix=none \
	    --formatted \
	    --break-blocks \
	    --exclude=mcx_bench.c \
		--exclude=mcx_lang.c \
	   "*.c" "*.h" "*.cpp" "*.cu"

.DEFAULT_GOAL := all

