# used to create breaks in error messages
define n


endef

###############################################################################
# Library locations

MATH ?=
BOOST ?= $(MATH)lib/boost_1.84.0
EIGEN ?= $(MATH)lib/eigen_3.4.0
OPENCL ?= $(MATH)lib/opencl_3.0.0
TBB ?= $(MATH)lib/tbb_2020.3
SUNDIALS ?= $(MATH)lib/sundials_6.1.1
BENCHMARK ?= $(MATH)lib/benchmark_1.5.1
GTEST ?= $(BENCHMARK)/googletest/googletest
CPPLINT ?= $(MATH)lib/cpplint_1.4.5

################################################################################
# SUNDIALS build rules
# Note: Files starting with f* are by SUNDIALS convention files needed for
#       Fortran bindings which we do not need for stan-math. Thus these targets
#       are ignored here. This convention was introduced with 4.0.
##
ifndef SUNDIALS_TARGETS

SUNDIALS_CVODES := $(patsubst %.c,%.o,\
  $(wildcard $(SUNDIALS)/src/cvodes/*.c) \
  $(filter-out $(SUNDIALS)/src/sundials/sundials_profiler.c, $(wildcard $(SUNDIALS)/src/sundials/*.c)) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/newton/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/fixedpoint/[^f]*.c))

SUNDIALS_IDAS := $(patsubst %.c,%.o,\
  $(wildcard $(SUNDIALS)/src/idas/*.c) \
  $(filter-out $(SUNDIALS)/src/sundials/sundials_profiler.c, $(wildcard $(SUNDIALS)/src/sundials/*.c)) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/newton/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/fixedpoint/[^f]*.c))

SUNDIALS_KINSOL := $(patsubst %.c,%.o, \
  $(wildcard $(SUNDIALS)/src/kinsol/*.c) \
  $(filter-out $(SUNDIALS)/src/sundials/sundials_profiler.c, $(wildcard $(SUNDIALS)/src/sundials/*.c)) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunmatrix/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/band/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunlinsol/dense/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/newton/[^f]*.c) \
  $(wildcard $(SUNDIALS)/src/sunnonlinsol/fixedpoint/[^f]*.c))

SUNDIALS_NVECSERIAL := $(patsubst %.c,%.o,\
  $(addprefix $(SUNDIALS)/src/, nvector/serial/nvector_serial.c sundials/sundials_math.c))

$(sort $(SUNDIALS_CVODES) $(SUNDIALS_IDAS) $(SUNDIALS_KINSOL) $(SUNDIALS_NVECSERIAL)) : CXXFLAGS = $(CXXFLAGS_SUNDIALS) $(CXXFLAGS_OS) $(CXXFLAGS_OPTIM_SUNDIALS) -O$(O) $(INC_SUNDIALS)
$(sort $(SUNDIALS_CVODES) $(SUNDIALS_IDAS) $(SUNDIALS_KINSOL) $(SUNDIALS_NVECSERIAL)) : CPPFLAGS = $(CPPFLAGS_SUNDIALS) $(CPPFLAGS_OS) $(CPPFLAGS_OPTIM_SUNDIALS) -O$(O)
$(sort $(SUNDIALS_CVODES) $(SUNDIALS_IDAS) $(SUNDIALS_KINSOL) $(SUNDIALS_NVECSERIAL)) : %.o : %.c
	@mkdir -p $(dir $@)
	$(COMPILE.cpp) -x c -include $(SUNDIALS)/include/stan_sundials_printf_override.hpp $< $(OUTPUT_OPTION)

$(SUNDIALS)/lib/libsundials_cvodes.a: $(SUNDIALS_CVODES)
	@mkdir -p $(dir $@)
	$(AR) -rs $@ $^

$(SUNDIALS)/lib/libsundials_idas.a: $(SUNDIALS_IDAS)
	@mkdir -p $(dir $@)
	$(AR) -rs $@ $^

$(SUNDIALS)/lib/libsundials_kinsol.a: $(SUNDIALS_KINSOL)
	@mkdir -p $(dir $@)
	$(AR) -rs $@ $^

$(SUNDIALS)/lib/libsundials_nvecserial.a: $(SUNDIALS_NVECSERIAL)
	@mkdir -p $(dir $@)
	$(AR) -rs $@ $^

SUNDIALS_TARGETS ?= $(SUNDIALS)/lib/libsundials_nvecserial.a $(SUNDIALS)/lib/libsundials_cvodes.a $(SUNDIALS)/lib/libsundials_idas.a $(SUNDIALS)/lib/libsundials_kinsol.a

STAN_SUNDIALS_HEADERS :=  $(call findfiles,$(MATH)stan,*cvodes*.hpp) $(call findfiles,$(MATH)stan,*idas*.hpp) $(call findfiles,$(MATH)stan,*kinsol*.hpp)
$(STAN_SUNDIALS_HEADERS) : $(SUNDIALS_TARGETS)

clean-sundials:
	@echo '  cleaning sundials targets'
	$(RM) $(wildcard $(sort $(SUNDIALS_CVODES) $(SUNDIALS_IDAS) $(SUNDIALS_KINSOL) $(SUNDIALS_NVECSERIAL) $(SUNDIALS_TARGETS)))
endif

############################################################
# TBB build rules
#
# TBB_CXX_TYPE can be icl, icc, gcc or clang; See tbb documentation for more info.
# For gcc and clang this is derived from stan makefile defaults automatically.
#
# TBB_CC is the C compiler to be used, which is by default derived here from the
# defined C++ compiler type. In case neither clang or gcc is used, then the CC
# variable is used if defined.
#
# Note that the tbb targets must not be build in parallel (so no concurrent
# build of tbb and tbbmalloc, for example). This is ensured here with proper
# dependencies.

ifndef TBB_LIB

ifeq ($(CXX_TYPE),mingw32-gcc)
  TBB_CXX_TYPE ?= gcc
endif
ifeq ($(CXX_TYPE),other)
  ifeq (,$(TBB_CXX_TYPE))
    $(error "Need to set TBB_CXX_TYPE for non-standard compiler other than gcc or clang.")
  endif
endif
TBB_CXX_TYPE ?= $(CXX_TYPE)

# Set c compiler used for the TBB
ifeq (clang,$(CXX_TYPE))
  TBB_CC ?= $(subst clang++,clang,$(CXX))
  TBB_CXXFLAGS ?= -Wno-unknown-warning-option -Wno-deprecated-copy $(CXXFLAGS_OPTIM_TBB) $(CXXFLAGS_FLTO_TBB)
endif
ifeq (gcc,$(CXX_TYPE))
  TBB_CC ?= $(subst g++,gcc,$(CXX))
  TBB_CXXFLAGS ?= -Wno-unknown-warning-option -Wno-deprecated-copy -Wno-missing-attributes -Wno-class-memaccess -Wno-sized-deallocation $(CXXFLAGS_OPTIM_TBB) $(CXXFLAGS_FLTO_TBB)
endif
TBB_CC ?= $(CC)

ifeq (,$(TBB_CC))
  $(error "Need to set TBB_CC to C compiler command for non-standard compiler other than gcc or clang.")
endif

# Set shell in order to use string matching for tbb-make-check
ifeq (Linux, $(OS))
  SHELL = /usr/bin/env bash
endif

ifeq (Windows_NT, $(OS))
  ifeq ($(IS_UCRT),true)
    TBB_CXXFLAGS += -D_UCRT
  endif
  # TBB does not have assembly code for Windows ARM64, so we need to use GCC builtins
	ifeq ($(ARM64),true)
		TBB_CXXFLAGS += -DTBB_USE_GCC_BUILTINS
		CXXFLAGS_TBB += -DTBB_USE_GCC_BUILTINS
		WINARM64 = true
	endif
	SH_CHECK := $(shell command -v sh 2>/dev/null)
	ifdef SH_CHECK
		WINDOWS_HAS_SH ?= true
	endif
	ifeq ($(WINDOWS_HAS_SH), true) # Stop TBB makefile changing SHELL on RTools
		SHELL = sh.exe
	endif
endif

# If brackets or spaces are found in MAKE on Windows
# we error, as those characters cause issues when building.
ifeq (Windows_NT, $(OS))
MAKE_ESCAPED := ${subst ),,${subst (,,$(lastword $(MAKE))}}
else
MAKE_ESCAPED := $(MAKE)
endif

$(TBB_BIN)/tbb-make-check:
ifeq ($(OS),Windows_NT)
ifneq ($(MAKE),$(MAKE_ESCAPED))
$(error '$nError:$n$nThe RTools toolchain is installed in a path with spaces or brackets.$nPlease reinstall the toolchain.$n$n')
endif
endif
	@mkdir -p $(TBB_BIN)
	touch $(TBB_BIN)/tbb-make-check


$(TBB_BIN)/tbb.def: $(TBB_BIN)/tbb-make-check
	@mkdir -p $(TBB_BIN)
	touch $(TBB_BIN)/version_$(notdir $(TBB))
	tbb_root="$(TBB_RELATIVE_PATH)" WINARM64="$(WINARM64)" CXX="$(CXX)" CC="$(TBB_CC)" LDFLAGS='$(LDFLAGS_TBB)' '$(MAKE)' -C "$(TBB_BIN)" -r -f "$(TBB_ABSOLUTE_PATH)/build/Makefile.tbb" compiler=$(TBB_CXX_TYPE) cfg=release stdver=$(CXXFLAGS_STANDARD)  CXXFLAGS="$(TBB_CXXFLAGS)"

$(TBB_BIN)/tbbmalloc.def: $(TBB_BIN)/tbb-make-check
	@mkdir -p $(TBB_BIN)
	tbb_root="$(TBB_RELATIVE_PATH)" WINARM64="$(WINARM64)" CXX="$(CXX)" CC="$(TBB_CC)" LDFLAGS='$(LDFLAGS_TBB)' '$(MAKE)' -C "$(TBB_BIN)" -r -f "$(TBB_ABSOLUTE_PATH)/build/Makefile.tbbmalloc" compiler=$(TBB_CXX_TYPE) cfg=release stdver=$(CXXFLAGS_STANDARD) malloc CXXFLAGS="$(TBB_CXXFLAGS)"

$(TBB_BIN)/libtbb.dylib: $(TBB_BIN)/tbb.def
$(TBB_BIN)/libtbbmalloc.dylib: $(TBB_BIN)/tbbmalloc.def
$(TBB_BIN)/libtbbmalloc_proxy.dylib: $(TBB_BIN)/tbbmalloc.def

$(TBB_BIN)/libtbb.so.2: $(TBB_BIN)/tbb.def
$(TBB_BIN)/libtbbmalloc.so.2: $(TBB_BIN)/tbbmalloc.def
$(TBB_BIN)/libtbbmalloc_proxy.so.2: $(TBB_BIN)/tbbmalloc.def

$(TBB_BIN)/tbb.dll: $(TBB_BIN)/tbb.def
$(TBB_BIN)/tbbmalloc.dll: $(TBB_BIN)/tbbmalloc.def
$(TBB_BIN)/tbbmalloc_proxy.dll: $(TBB_BIN)/tbbmalloc.def

clean-tbb:
	@echo '  cleaning Intel TBB targets'
	$(RM) -rf $(TBB_BIN)

endif

############################################################
# MPI build rules

ifdef STAN_MPI

##
# Boost build options
BOOST_PARALLEL_JOBS ?= 1


$(BOOST)/user-config.jam:
	echo "# In case of a compiler mismatch used by mpicxx and" >> $(BOOST)/user-config.jam
	echo "# the compiler used for Stan, consider configuring" >> $(BOOST)/user-config.jam
	echo "# the boost toolset here" >> $(BOOST)/user-config.jam
	echo "# Moreover, should your mpicxx command live in a" >> $(BOOST)/user-config.jam
	echo "# in a non-standard directory, then consider to tell" >> $(BOOST)/user-config.jam
	echo "# boost mpi using this syntax:" >> $(BOOST)/user-config.jam
	echo "#using mpi : /path/to/mpicxx ;" >> $(BOOST)/user-config.jam
	echo "using mpi ;" >> $(BOOST)/user-config.jam

$(BOOST)/stage/lib/libboost_serialization.so: $(BOOST)/stage/lib/libboost_mpi.so
$(BOOST)/stage/lib/libboost_mpi.so: $(BOOST)/user-config.jam
	@mkdir -p $(dir $@)
	cd $(BOOST); ./bootstrap.sh
	cd $(BOOST); ./b2 --user-config=user-config.jam --layout=system --with-mpi --with-serialization -j$(BOOST_PARALLEL_JOBS) variant=release link=shared threading=multi runtime-link=shared hardcode-dll-paths=true dll-path="$(BOOST_LIBRARY_ABSOLUTE_PATH)" cxxstd=11

$(BOOST)/stage/lib/libboost_serialization.dylib: $(BOOST)/stage/lib/libboost_mpi.dylib
$(BOOST)/stage/lib/libboost_mpi.dylib: $(BOOST)/user-config.jam
	@mkdir -p $(dir $@)
	cd $(BOOST); ./bootstrap.sh
	cd $(BOOST); ./b2 --user-config=user-config.jam --layout=system --with-mpi --with-serialization -j$(BOOST_PARALLEL_JOBS) variant=release link=shared threading=multi runtime-link=shared hardcode-dll-paths=true dll-path="$(BOOST_LIBRARY_ABSOLUTE_PATH)" cxxstd=11
	install_name_tool -add_rpath "$(BOOST_LIBRARY_ABSOLUTE_PATH)" "$(BOOST)/stage/lib/libboost_serialization.dylib"
	install_name_tool -id @rpath/libboost_serialization.dylib "$(BOOST)/stage/lib/libboost_serialization.dylib"
	install_name_tool -add_rpath "$(BOOST_LIBRARY_ABSOLUTE_PATH)" "$(BOOST)/stage/lib/libboost_mpi.dylib"
	install_name_tool -change libboost_serialization.dylib @rpath/libboost_serialization.dylib "$(BOOST)/stage/lib/libboost_mpi.dylib"
	install_name_tool -id @rpath/libboost_mpi.dylib "$(BOOST)/stage/lib/libboost_mpi.dylib"

MPI_TEMPLATE_INSTANTIATION_CPP := $(call findfiles,$(MATH)stan,mpi_*_inst.cpp) $(call findfiles,$(MATH)stan,mpi_*_def.cpp)
MPI_TEMPLATE_INSTANTIATION := $(MPI_TEMPLATE_INSTANTIATION_CPP:%.cpp=%.o)

$(MPI_TEMPLATE_INSTANTIATION) : CXXFLAGS += -fPIC

clean-mpi:
	@echo '  cleaning mpi targets'
	$(RM) $(wildcard $(LIBMPI) $(MPI_INSTANTIATION))
	$(RM) -r $(wildcard $(BOOST)/stage/lib $(BOOST)/bin.v2 $(BOOST)/tools/build/src/engine/bootstrap/ $(BOOST)/tools/build/src/engine/bin.* $(BOOST)/project-config.jam* $(BOOST)/b2 $(BOOST)/bjam $(BOOST)/bootstrap.log)

endif

############################################################
# Google Test:
#   Build the google test library.
$(GTEST)/src/gtest-all.o: CXXFLAGS += $(CXXFLAGS_GTEST)
$(GTEST)/src/gtest-all.o: CPPFLAGS += $(CPPFLAGS_GTEST)
$(GTEST)/src/gtest-all.o: INC += $(INC_GTEST)



############################################################
# Clean all libraries

.PHONY: clean-libraries clean-sundials clean-mpi clean-tbb
clean-libraries: clean-sundials clean-mpi clean-tbb
