# MAKEFILE for scs
include scs.mk

# Base object files always included
BASE_SCS_OBJECTS = \
    src/util.o src/cones.o src/exp_cone.o src/aa.o src/rw.o src/linalg.o src/ctrlc.o src/scs_version.o src/normalize.o

# Spectral cones files, only included if USE_SPECTRAL_CONES=1
SPECTRAL_CONES_OBJECTS = \
    src/spectral_cones/logdeterminant/log_cone_Newton.o \
    src/spectral_cones/logdeterminant/log_cone_IPM.o \
    src/spectral_cones/logdeterminant/log_cone_wrapper.o \
    src/spectral_cones/logdeterminant/logdet_cone.o \
    src/spectral_cones/nuclear/ell1_cone.o \
    src/spectral_cones/nuclear/nuclear_cone.o \
    src/spectral_cones/sum-largest/sum_largest_cone.o \
    src/spectral_cones/sum-largest/sum_largest_eval_cone.o \
    src/spectral_cones/util_spectral_cones.o

# Now decide which objects to include:
ifneq ($(USE_LAPACK),0)
  ifneq ($(USE_SPECTRAL_CONES),0)
    SCS_OBJECTS = $(BASE_SCS_OBJECTS) $(SPECTRAL_CONES_OBJECTS)
  else
    SCS_OBJECTS = $(BASE_SCS_OBJECTS)
  endif
else
  SCS_OBJECTS = $(BASE_SCS_OBJECTS)
endif

SCS_O = src/scs.o
SCS_INDIR_O = src/scs_indir.o
SCS_MKL_O = src/scs_mkl.o

SRC_FILES = $(wildcard src/*.c)
INC_FILES = $(wildcard include/*.h)

AMD_SOURCE = $(wildcard $(EXTSRC)/amd/*.c)
LDL_SOURCE = $(EXTSRC)/qdldl/qdldl.c
AMD_OBJS = $(AMD_SOURCE:.c=.o)
LDL_OBJS = $(LDL_SOURCE:.c=.o)
TARGETS = $(OUT)/demo_socp_indirect $(OUT)/demo_socp_direct $(OUT)/run_from_file_indirect $(OUT)/run_from_file_direct

.PHONY: default

default: $(TARGETS) $(OUT)/libscsdir.a $(OUT)/libscsindir.a $(OUT)/libscsdir.$(SHARED) $(OUT)/libscsindir.$(SHARED)
	@echo "****************************************************************************************"
	@echo "Successfully compiled scs, copyright Brendan O'Donoghue 2012."
	@echo "To test, run 'make test' and then '$(OUT)/run_tests_direct'."
	@echo "**********************************************************************************"
ifneq ($(USE_LAPACK), 0)
	@echo "Compiled with blas and lapack, can solve LPs, SOCPs, SDPs, ECPs, and PCPs"
else
	@echo "NOT compiled with blas/lapack, cannot solve SDPs (can solve LPs, SOCPs, ECPs, and PCPs)."
	@echo "To solve SDPs, install blas and lapack, then edit scs.mk to set USE_LAPACK=1"
	@echo "and point to the library install locations, and recompile with 'make purge', 'make'."
endif
ifneq ($(USE_SPECTRAL_CONES), 0)
	@echo "Compiled with spectral cones extension enabled (Daniel Cederberg 2025)"
else
	@echo "Spectral cones extension is NOT enabled. To enable spectral cones, edit"
	@echo "scs.mk to set USE_SPECTRAL_CONES=1 and recompile."
endif
	@echo "****************************************************************************************"

$(SCS_O): src/scs.c $(INC_FILES)
	$(CC) $(CFLAGS) -c $< -o $@

$(SCS_INDIR_O): src/scs.c $(INC_FILES)
	$(CC) $(CFLAGS) -DINDIRECT=1 -c $< -o $@

$(SCS_MKL_O): src/scs.c $(INC_FILES)
	$(CC) $(CFLAGS) -DSCS_MKL=1 -c $< -o $@

%.o : src/%.c
	$(CC) $(CFLAGS) -c $< -o $@

src/util.o	: src/util.c $(INC_FILES)
src/cones.o	: src/cones.c $(INC_FILES)
src/exp_cone.o	: src/exp_cone.c $(INC_FILES)
src/aa.o	: src/aa.c $(INC_FILES)
src/rw.o	: src/rw.c $(INC_FILES)
src/linalg.o: src/linalg.c $(INC_FILES)
src/ctrlc.o : src/ctrlc.c $(INC_FILES)
src/scs_version.o: src/scs_version.c $(INC_FILES)

$(DIRSRC)/private.o: $(DIRSRC)/private.c  $(DIRSRC)/private.h
$(INDIRSRC)/private.o: $(INDIRSRC)/private.c $(INDIRSRC)/private.h
$(DENSESRC)/private.o: $(DENSESRC)/private.c  $(DENSESRC)/private.h
$(MKLSRC)/private.o: $(MKLSRC)/private.c  $(MKLSRC)/private.h
$(CUDSSSRC)/private.o: $(CUDSSSRC)/private.c  $(CUDSSSRC)/private.h
	$(CUCC) $(INCLUDE) $(CUDSS_FLAGS) -I$(CUDSSSRC) -c $(CUDSSSRC)/private.c -o $@
$(ACCELSRC)/private.o: $(ACCELSRC)/private.c  $(ACCELSRC)/private.h
$(LINSYS)/scs_matrix.o: $(LINSYS)/scs_matrix.c $(LINSYS)/scs_matrix.h
$(LINSYS)/csparse.o: $(LINSYS)/csparse.c $(LINSYS)/csparse.h

$(OUT)/libscsdir.a: $(SCS_O) $(SCS_OBJECTS) $(DIRSRC)/private.o $(AMD_OBJS) $(LDL_OBJS) $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/libscsindir.a: $(SCS_INDIR_O) $(SCS_OBJECTS) $(INDIRSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/libscsdense.a: $(SCS_O) $(SCS_OBJECTS) $(DENSESRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/libscsmkl.a: $(SCS_MKL_O) $(SCS_OBJECTS) $(MKLSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/libscscudss.a: $(SCS_O) $(SCS_OBJECTS) $(CUDSSSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/libscsaccel.a: $(SCS_O) $(SCS_OBJECTS) $(ACCELSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

# Link a shared library as its versioned file with the ABI version as its
# soname/install_name, then point the unversioned name at it (Windows: the
# DLL itself, unversioned). $(1): extra link flags.
V = $(call versioned,$@)
define link_shared
	mkdir -p $(OUT)
	$(CC) $(CFLAGS) -shared -Wl,$(SONAME),$(notdir $(V)) -o $(V) $^ $(LDFLAGS) $(1)
	[ "$(V)" = "$@" ] || ln -sf $(notdir $(V)) $@
endef

$(OUT)/libscsdir.$(SHARED): $(SCS_O) $(SCS_OBJECTS) $(DIRSRC)/private.o $(AMD_OBJS) $(LDL_OBJS) $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,$(BLASLDFLAGS))

$(OUT)/libscsindir.$(SHARED): $(SCS_INDIR_O) $(SCS_OBJECTS) $(INDIRSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,$(BLASLDFLAGS))

$(OUT)/libscsdense.$(SHARED): $(SCS_O) $(SCS_OBJECTS) $(DENSESRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,$(BLASLDFLAGS))

$(OUT)/libscsmkl.$(SHARED): $(SCS_MKL_O) $(SCS_OBJECTS) $(MKLSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,$(MKLFLAGS))

$(OUT)/libscscudss.$(SHARED): $(SCS_O) $(SCS_OBJECTS) $(CUDSSSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,$(CULDFLAGS))

$(OUT)/libscsaccel.$(SHARED): $(SCS_O) $(SCS_OBJECTS) $(ACCELSRC)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o
	$(call link_shared,-framework Accelerate)

$(OUT)/demo_socp_direct: test/random_socp_prob.c $(OUT)/libscsdir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS)

$(OUT)/demo_socp_indirect: test/random_socp_prob.c $(OUT)/libscsindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS)

$(OUT)/demo_socp_dense: test/random_socp_prob.c $(OUT)/libscsdense.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS)

$(OUT)/demo_socp_mkl: test/random_socp_prob.c $(OUT)/libscsmkl.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(MKLFLAGS)

$(OUT)/demo_socp_cudss: test/random_socp_prob.c $(OUT)/libscscudss.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) $(CUDSS_LDFLAGS)

$(OUT)/demo_socp_accelerate: test/random_socp_prob.c $(OUT)/libscsaccel.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -framework Accelerate

$(OUT)/run_from_file_direct: test/run_from_file.c $(OUT)/libscsdir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS)

$(OUT)/run_from_file_indirect: test/run_from_file.c $(OUT)/libscsindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS)

$(OUT)/run_from_file_gpu_indirect: test/run_from_file.c $(OUT)/libscsgpuindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) $(CULDFLAGS)

# basic testing
.PHONY: test
TEST_TARGETS = $(OUT)/run_tests_indirect $(OUT)/run_tests_direct
ifneq ($(USE_LAPACK),0)
TEST_TARGETS += $(OUT)/run_tests_dense
endif
ifeq ($(UNAME), Darwin)
ifeq ($(DLONG), 0)
TEST_TARGETS += $(OUT)/run_tests_accelerate
endif
endif
test: $(TEST_TARGETS)
$(OUT)/run_tests_indirect: test/run_tests.c $(OUT)/libscsindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) -Itest
$(OUT)/run_tests_direct: test/run_tests.c $(OUT)/libscsdir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) -Itest
$(OUT)/test_rw_settings: test/rw_settings.c test/problems/test_rw_settings.h $(OUT)/libscsdir.a
	$(CC) $(CFLAGS) -o $@ $< $(OUT)/libscsdir.a $(LDFLAGS) $(BLASLDFLAGS) -Itest
$(OUT)/run_tests_dense: test/run_tests.c $(OUT)/libscsdense.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) -Itest
$(OUT)/run_tests_mkl: test/run_tests.c $(OUT)/libscsmkl.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(MKLFLAGS) -Itest

$(OUT)/run_tests_cudss: test/run_tests.c $(OUT)/libscscudss.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) $(CUDSS_LDFLAGS) -Itest

$(OUT)/run_tests_accelerate: test/run_tests.c $(OUT)/libscsaccel.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -framework Accelerate -Itest

.PHONY: test_gpu
test_gpu: $(OUT)/run_tests_gpu_indirect

.PHONY: mkl
mkl: mklroot $(OUT)/libscsmkl.a $(OUT)/libscsmkl.$(SHARED) $(OUT)/run_tests_mkl $(OUT)/demo_socp_mkl
mklroot:
ifndef MKLROOT
	$(error MKLROOT is undefined, set MKLROOT to the MKL install location)
endif

.PHONY: cudss
cudss: $(OUT)/libscscudss.a $(OUT)/libscscudss.$(SHARED) $(OUT)/run_tests_cudss $(OUT)/demo_socp_cudss

.PHONY: accelerate
accelerate: $(OUT)/libscsaccel.a $(OUT)/libscsaccel.$(SHARED) $(OUT)/run_tests_accelerate $(OUT)/demo_socp_accelerate

$(OUT)/run_tests_gpu_indirect: test/run_tests.c $(OUT)/libscsgpuindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) $(CULDFLAGS) -Itest

# REQUIRES GPU AND CUDA INSTALLED
# Note: only the GPU indirect solver is currently implemented.
gpu: gpu_indirect

gpu_indirect: $(OUT)/demo_socp_gpu_indirect $(OUT)/libscsgpuindir.$(SHARED) $(OUT)/libscsgpuindir.a $(OUT)/run_from_file_gpu_indirect

$(LINSYS)/gpu/gpu.o: $(LINSYS)/gpu/gpu.c
	$(CC) -c -o $@ $^ $(CUDAFLAGS)

$(GPUINDIR)/private.o: $(GPUINDIR)/private.c
	$(CC) -c -o $@ $^ $(CUDAFLAGS)

$(OUT)/libscsgpuindir.$(SHARED): $(SCS_INDIR_O) $(SCS_OBJECTS) $(GPUINDIR)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o $(LINSYS)/gpu/gpu.o
	$(call link_shared,$(BLASLDFLAGS) $(CULDFLAGS))

$(OUT)/libscsgpuindir.a: $(SCS_INDIR_O) $(SCS_OBJECTS) $(GPUINDIR)/private.o $(LINSYS)/scs_matrix.o $(LINSYS)/csparse.o $(LINSYS)/gpu/gpu.o
	mkdir -p $(OUT)
	$(ARCHIVE) $@ $^
	- $(RANLIB) $@

$(OUT)/demo_socp_gpu_indirect: test/random_socp_prob.c $(OUT)/libscsgpuindir.a
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BLASLDFLAGS) $(CULDFLAGS)

.PHONY: clean purge
clean:
	@rm -rf $(TARGETS) $(SCS_O) $(SCS_INDIR_O) $(SCS_MKL_O) $(SCS_OBJECTS) $(AMD_OBJS) $(LDL_OBJS) $(LINSYS)/*.o $(DIRSRC)/*.o $(INDIRSRC)/*.o $(DENSESRC)/*.o $(MKLSRC)/*.o $(ACCELSRC)/*.o $(GPUDIR)/*.o $(GPUINDIR)/*.o $(LINSYS)/gpu/*.o src/spectral_cones/*.o src/spectral_cones/*/*.o
	@rm -rf $(OUT)/*.dSYM
	@rm -rf matlab/*.mex*
	@rm -rf .idea
	@rm -rf python/*.pyc
	@rm -rf python/build
purge: clean
	@rm -rf $(OUT)

INSTALL_INC_FILES = $(INC_FILES)

INSTALL_TARGETS = $(OUT)/libscsdir.a $(OUT)/libscsindir.a $(OUT)/libscsdir.$(SHARED) $(OUT)/libscsindir.$(SHARED)
INSTALL_GPU_TARGETS = $(OUT)/libscsgpuindir.a $(OUT)/libscsgpuindir.$(SHARED)

# shared libraries are installed under their versioned names with the
# unversioned symlink alongside
INSTALL_SHARED = $(filter %.$(SHARED),$(INSTALL_TARGETS))
INSTALL_GPU_SHARED = $(filter %.$(SHARED),$(INSTALL_GPU_TARGETS))
# the versioned files, plus the unversioned symlinks copied as symlinks
install_shared = $(INSTALL) -m 644 $(call versioned,$(1)) $(INSTALL_LIB_DIR) && \
	{ [ "$(call versioned,$(1))" = "$(1)" ] || cp -RP $(1) $(INSTALL_LIB_DIR); }

INSTALL_INC_DIR = $(DESTDIR)$(PREFIX)/include/scs/
INSTALL_LIB_DIR = $(DESTDIR)$(PREFIX)/lib/

.PHONY: dense
dense: $(OUT)/libscsdense.a $(OUT)/libscsdense.$(SHARED) $(OUT)/run_tests_dense $(OUT)/demo_socp_dense

.PHONY: install install_gpu direct indirect
install: $(INSTALL_INC_FILES) $(INSTALL_TARGETS)
	$(INSTALL) -d $(INSTALL_INC_DIR) $(INSTALL_LIB_DIR)
	$(INSTALL) -m 644 $(INSTALL_INC_FILES) $(INSTALL_INC_DIR)
	$(INSTALL) -m 644 $(filter-out %.$(SHARED),$(INSTALL_TARGETS)) $(INSTALL_LIB_DIR)
	$(call install_shared,$(INSTALL_SHARED))
install_gpu: $(INSTALL_INC_FILES) $(INSTALL_GPU_TARGETS)
	$(INSTALL) -d $(INSTALL_INC_DIR) $(INSTALL_LIB_DIR)
	$(INSTALL) -m 644 $(INSTALL_INC_FILES) $(INSTALL_INC_DIR)
	$(INSTALL) -m 644 $(filter-out %.$(SHARED),$(INSTALL_GPU_TARGETS)) $(INSTALL_LIB_DIR)
	$(call install_shared,$(INSTALL_GPU_SHARED))
direct: $(OUT)/libscsdir.$(SHARED) $(OUT)/demo_socp_direct $(OUT)/run_from_file_direct $(OUT)/run_tests_direct
indirect: $(OUT)/libscsindir.$(SHARED) $(OUT)/demo_socp_indirect $(OUT)/run_from_file_indirect $(OUT)/run_tests_indirect
