# Makefile for MEQ
# Must be run from meq directory
#
# To choose a particular version of MATLAB/MEX
#   define either the MATPATH or MATLAB variable
#   to point to the root of your MATLAB installation
#   i.e. $(MATPATH)/bin/matlab should exist
#
# [+MEQ MatlabEQuilibrium Toolbox+]

#    Copyright 2022-2025 Swiss Plasma Center EPFL
#
#   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.

# Set up MATLAB-related path
include genlib/mexargs/Makefile.inc
# Export some variables for reuse in genlib's Makefile
export CC MATPATH

# Phony targets
.PHONY: clean clear csf mex lib librt ld rt scd all tbx genlib doc pub

all-nort: tbx doc csf

all: all-nort rt

tbx: lib mex genlib

rt: librt rtwmakecfg.m ld

doc: pub

MEQROOT :=$(shell pwd)
BLASLIB ?=
# Default is to link to MATLAB's mwblas and mwlapack.
# alternatively: BLASLIB=MKL links to Intel's MKL, BLASLIB=CBLAS links to CBLAS/LAPACKE

# Include OS-specific Makefile, if exists.
ifneq (,$(wildcard Makefile.$(OS)))
include Makefile.$(OS)
endif

# Type of build
BUILD?=optimized
ifeq ($(BUILD),$(filter-out optimized debug,$(BUILD)))
  $(error Possible values for BUILD are optimized and debug, received BUILD="$(BUILD)")
endif

# Default compiler
ifdef USE_OCTAVE
  # Use gcc for Octave
  CC=gcc
else ifneq (,$(shell which icx))
  # For MATLAB prefer new Intel Clang/LLVM based compiler
  CC=icx
else ifneq (,$(shell which icc))
  # Next use classic Intel compiler
  CC=icc
else
  # Otherwise fall-back to GCC
  CC=gcc
endif
# Use classic Intel compiler for RT build
CCRT=icc

# Compiler flags
ifeq ($(BUILD),optimized)
  CFLAGS    +=-O3
else ifeq ($(BUILD),debug)
  CFLAGS    +=-g -O0 -DDEBUG
  MEXOPTIONS+=-g
  MEX_CFLAGS+=-O0
  MEX_LDFLAGS+=-O0
endif
ifeq ($(CC),icx)
  # Disable Floating-Point optimizations that are not value-safe
  CFLAGS +=-fp-model precise
endif
CFLAGS   +=-Iinclude -std=c99 -fPIC -Wall $(SYSROOT)
CFLAGSRT +=-Iinclude -std=c99 -DMKL -fPIC -O2 $(SYSROOT)
ifeq ($(CCRT),icc)
  CFLAGSRT +=-axAVX
else
  CFLAGSRT +=-mavx
endif
MEX_CFLAGS+=-Iinclude -std=c99 -fPIC -Wall $(MEX_CFLAGS_HOST)

# Enforce all compiler warnings
ifneq (no,$(WERROR))
  CFLAGS+= -Werror
  MEX_CFLAGS+= -Werror
endif

MEXBLD    =$(MEXCMD) $(MEXOPTIONS)

ifeq ($(BLASLIB),CBLAS)
  # CBLAS/LAPACKE (does not have vector mathematics - use private library vblas)
  LINKLINE=libmeq.a libv/libvblas.a ${LAPACKLIBS} ${CBLASLIBS} 
  CFLAGS+=-DCBLAS ${LAPACKCFLAGS} ${CBLASCFLAGS}
  MEX_CFLAGS+=-DCBLAS ${LAPACKCFLAGS} ${CBLASCFLAGS}
  ifeq ($(CC),gcc)
    # gcc math library
    LINKLINE+=-lm
  endif
  LIBTARGETS+=libv/libvblas.a
  LIBRTTARGETS+=libv/libvblas.a
else ifeq ($(BLASLIB),MKL)
  # Intel MKL
  CFLAGS+=-DMKL
  MEX_CFLAGS+=-DMKL
else
  # MATLAB mwblas/mwlapack (does not have vector mathematics - use private library vblas)
  LINKLINE+=libmeq.a libv/libvblas.a libmatmkl.a -lmwblas -lmwlapack
  CFLAGS+=-I$(MATPATH)/extern/include
  LIBTARGETS+=libv/libvblas.a libmatmkl.a
  LIBRTTARGETS+=libv/libvblas.a libmatmkl.a
endif

LIBTARGETS   += libmeq.a
LIBRTTARGETS += libmeqrt.a

ifndef USE_OCTAVE
  MATOPT := -singleCompThread $(MATOPT)
endif

ifdef USE_OCTAVE
  # additional path needed to make getReport() available
  ADDOCTAVEPATH = addpath('_octave');
endif

# List of subdirectories to create
SUBDIRS=csfc obj

# List of folders to search for source files or prerequisites
VPATH=libmeq mexc matmkl libv

## RECIPES

# genlib
genlib:
	$(MAKE) -C genlib

# lib
LIB      =gszr nfdb asxy bavx fbnd pdom bbox iata uata fl4p resp bfct bf3p bf3i bfef bfab bfpr fsgi fsg2 vizr cizr bslv rtci ipm2 ipm4 ipmh ipmj wclk shap asgn locQ minQ qint bint
LIBSRC   =$(LIB:%=libmeq/%.c)
LIBOBJ   =$(LIB:%=obj/d%.o  ) $(LIB:%=obj/s%.o  )
LIBOBJRT =$(LIB:%=obj/d%rt.o) $(LIB:%=obj/s%rt.o)
LIBV     =vblas
LIBVOBJ  =$(LIBV:%=obj/d%.o  ) $(LIBV:%=obj/s%.o  )
LIBM     =axpy copy scal dot gemv spmv gemm syrk pptrf pptrs sysv
LIBMOBJ  =$(LIBM:%=obj/d%.o  ) $(LIBM:%=obj/s%.o  )

include/meq.h: mkmeqh.m include/meqh.h $(LIBSRC)
	$(MATCMD) "try, $(ADDOCTAVEPATH) mkmeqh('$(LIB)');exit(0); catch ME, disp(getReport(ME));exit(1); end"

obj/d%.o: %.c | obj
	$(CC) -c $< -o $@ $(CFLAGS)

obj/s%.o: %.c | obj
	$(CC) -c $< -o $@ $(CFLAGS) -DSINGLE

obj/d%rt.o: %.c | obj
	$(CCRT) -c $< -o $@ $(CFLAGSRT)

obj/s%rt.o: %.c | obj
	$(CCRT) -c $< -o $@ $(CFLAGSRT) -DSINGLE

$(LIBOBJ) $(LIBOBJRT): include/meq.h
$(LIBMOBJ): include/meqh.h

libmeq.a: $(LIBOBJ)
libmeqrt.a: $(LIBOBJRT)
libv/libvblas.a: $(LIBVOBJ)
libmatmkl.a: $(LIBMOBJ)
%.a:
	ar rs $@ $^

lib: $(LIBTARGETS)
librt: $(LIBRTTARGETS)

# ld - Matlab 8.3 mex command refuses .ld extension but pass to ld .lib files
MKLLIBGRP   =libmkl_intel_lp64.a libmkl_intel_thread.a libmkl_core.a
ifdef MKLLIBGRPRT32
MKLLIBGRPRT =libmkl_intel.a      libmkl_sequential.a   libmkl_core.a
else
MKLLIBGRPRT =libmkl_intel_lp64.a libmkl_sequential.a   libmkl_core.a
endif

meqld.lib: meqld.lib.m4
	m4 --define MEQROOT="$(MEQROOT)" --define MKLLIBGRP="$(MKLLIBGRP)" $^ > $@
meqldrt.lib: meqld.lib.m4
	m4 --define MEQROOT="$(MEQROOT)" --define MKLLIBGRP="$(MKLLIBGRPRT)" --define RT $^ > $@
ld: meqld.lib meqldrt.lib

# mex
MEX    =gszr nfdb asxy bavx fbnd pdom iata uata fl4p resp bbox bf3p bf3i bfef bfpr bfab vizr cizr bslv rtci fsgi fsg2 chol ipm2 ipm4 ipmh ipmj shap locS locR minQ qint bint
MEXSRC =$(MEX:%=mexc/%mex.c)
MEXMEX =$(MEX:%=%mex.$(MEXEXT))
$(MEXMEX): $(LIBTARGETS)
bf3pmex.$(MEXEXT) bf3imex.$(MEXEXT) bfefmex.$(MEXEXT) bfabmex.$(MEXEXT): include/bfctmex.h
%.$(MEXEXT):%.c
	$(MEXBLD) -output $@ $< $(LINKLINE) 
mex: $(MEXMEX)

# publish documentation
pub: tbx
	$(subst -r,-batch,$(MATCMD)) "run('documentation/publish_documentation.m');"

# csf
CSFSMKL =vvuv vinv vdivuv mdivuv mvunv mvutv mmunvn mmutvn mmunvt minv
CSFDMKL =mvunv mmunvn mmunvt
CSFSMEQ =gszr nfdb asxy bavx bavx2 fbnd fl4p pdom bbox bf3p1 bf3p3 bf3p6 bf3p8 bf3i1 bf3i2 bf3i3 bf3i4 bf3i6 bfef1 bfef3 bfef6 bfpr resp bslv iata rtci rtci1 fsgi vizr ipmh ipmj wclk shap asgn locS locR minQ qint bint
CSFSSRC =$(CSFSMKL:%=csfc/s%csf.c) $(CSFSMEQ:%=csfc/s%csf.c)
CSFSMEX =$(CSFSMKL:%=s%csf.$(MEXEXT)) $(CSFSMEQ:%=s%csf.$(MEXEXT))

CSFDMEQ =bslv iata ipmh ipmj
CSFDSRC =$(CSFDMKL:%=csfc/d%csf.c) $(CSFDMEQ:%=csfc/d%csf.c)
CSFDMEX =$(CSFDMKL:%=d%csf.$(MEXEXT)) $(CSFDMEQ:%=d%csf.$(MEXEXT))

$(CSFSMEX): $(LIBTARGETS)
$(CSFDMEX): $(LIBTARGETS)

# Only a pattern rule with one input and multiple outputs is correctly handled by make (including parallel builds)
CSFPAT :=$(CSFSMKL:%=csfc/s%%.c) $(CSFSMEQ:%=csfc/s%%.c) $(CSFDMKL:%=csfc/d%%.c) $(CSFDMEQ:%=csfc/d%%.c)
# But the first file becomes an intermediate file, so it needs to be protected against deletion
.SECONDARY: $(CSFSSRC) $(CSFDSRC)
$(CSFPAT) rtwmakecfg_%.m: mk%.m | csfc
	$(MATCMD) "try, mkcsf;exit(0); catch ME, disp(getReport(ME));exit(1); end"

.INTERMEDIATE: rtwmakecfg_csf.m
rtwmakecfg.m: rtwmakecfg_csf.m
	mv $^ $@

%.$(MEXEXT):csfc/%.c
	$(MEXBLD) -output $@ $< $(LINKLINE)
csf: $(CSFSMEX) $(CSFDMEX)

$(SUBDIRS):
	mkdir -p $@

clean: 
	$(RM) obj/*.o *.lib *.a include/meq.h
	$(RM) -r lib*.a $(SUBDIRS) *.mex* *.tlc libv/libvblas.a rtwmakecfg.m
	$(MAKE) -C genlib clean
	find html -mindepth 1 -not -name 'helptoc.xml' -delete

