# CMakeLists.txt file for scs
# This software may be modified and distributed under the terms of the MIT License

cmake_minimum_required(VERSION 3.5)

project(scs
  LANGUAGES C
  VERSION 2.1.4)

# Defines the CMAKE_INSTALL_LIBDIR, CMAKE_INSTALL_BINDIR and many other useful macros.
# See https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html
include(GNUInstallDirs)

# Control where libraries and executables are placed during the build.
# With the following settings executables are placed in <the top level of the
# build tree>/bin and libraries/archives in <top level of the build tree>/lib.
# This is particularly useful to run ctests on libraries built on Windows
# machines: tests, which are executables, are placed in the same folders of
# dlls, which are treated as executables as well, so that they can properly
# find the libraries to run. This is a because of missing RPATH on Windows.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")

# To build shared libraries in Windows, we set CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to TRUE.
# See https://cmake.org/cmake/help/v3.4/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.html
# See https://blog.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

# Under MSVC, we set CMAKE_DEBUG_POSTFIX to "d" to add a trailing "d" to library
# built in debug mode. In this Windows user can compile, build and install the
# library in both Release and Debug configuration avoiding naming clashes in the
# installation directories.
if(MSVC)
  set(CMAKE_DEBUG_POSTFIX "d")
endif()

# Build position independent code.
# Position Independent Code (PIC) is commonly used for shared libraries so that
# the same shared library code can be loaded in each program address space in a
# location where it will not overlap with any other uses of such memory.
# In particular, this option avoids problems occurring when a process wants to
# load more than one shared library at the same virtual address.
# Since shared libraries cannot predict where other shared libraries could be
# loaded, this is an unavoidable problem with the traditional shared library
# concept.
# Generating position-independent code is often the default behavior for most
# modern compilers.
# Moreover linking a static library that is not built with PIC from a shared
# library will fail on some compiler/architecture combinations.
# Further details on PIC can be found here:
# https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Disable C and C++ compiler extensions.
# C/CXX_EXTENSIONS are ON by default to allow the compilers to use extended
# variants of the C/CXX language.
# However, this could expose cross-platform bugs in user code or in the headers
# of third-party dependencies and thus it is strongly suggested to turn
# extensions off.
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS OFF)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

### Options
# Shared/Dynamic or Static library?
option(BUILD_SHARED_LIBS "Build libraries as shared as opposed to static" ON)

# Enable RPATH support for installed binaries and libraries
include(AddInstallRPATHSupport)
add_install_rpath_support(BIN_DIRS "${CMAKE_INSTALL_FULL_BINDIR}"
  LIB_DIRS "${CMAKE_INSTALL_FULL_LIBDIR}"
  INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}"
  USE_LINK_PATH)

# Encourage user to specify a build type (e.g. Release, Debug, etc.), otherwise set it to Release.
if(NOT CMAKE_CONFIGURATION_TYPES)
  if(NOT CMAKE_BUILD_TYPE)
    message(STATUS "Setting build type to 'Release' as none was specified.")
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY VALUE "Release")
  endif()
endif()


# Build test related commands?
option(BUILD_TESTING "Create tests using CMake" OFF)
if(BUILD_TESTING)
  enable_testing()
endif()

### Some variables useful for sampling the building process
# Note that the GPU profile is not compiled.
set(LINSYS linsys)
set(DIRSRC ${LINSYS}/cpu/direct)
set(INDIRSRC ${LINSYS}/cpu/indirect)
set(EXTERNAL ${LINSYS}/external)


# Common source files
set(${PROJECT_NAME}_SRC
  src/aa.c
  src/cones.c
  src/ctrlc.c
  src/linalg.c
  src/normalize.c
  src/rw.c
  src/scs.c
  src/scs_version.c
  src/util.c
  ${LINSYS}/csparse.c
  ${LINSYS}/scs_matrix.c)

# Common header files
set(${PROJECT_NAME}_HDR
  include/aa.h
  include/cones.h
  include/ctrlc.h
  include/glbopts.h
  include/linalg.h
  include/linsys.h
  include/normalize.h
  include/rw.h
  include/scs.h
  include/scs_blas.h
  include/util.h
  ${LINSYS}/csparse.h
  ${LINSYS}/scs_matrix.h)

# get all the c file in amd/external
file(GLOB ${PROJECT_NAME}_AMD_EXTERNAL_SRC
  ${EXTERNAL}/amd/*.c
  )

# get all the h file in amd/external
file(GLOB ${PROJECT_NAME}_AMD_EXTERNAL_HDR
  ${EXTERNAL}/amd/*.h
  )

# get all the c file in amd/external
file(GLOB ${PROJECT_NAME}_LDL_EXTERNAL_HDR
  ${EXTERNAL}/qdldl/*.h
  )

### Direct method
# Here we compile the direct method library
set(${PROJECT_NAME}_DIRECT ${PROJECT_NAME}dir)
add_library(${${PROJECT_NAME}_DIRECT}
  ${${PROJECT_NAME}_HDR}
  ${${PROJECT_NAME}_SRC}
  ${DIRSRC}/private.c
  ${DIRSRC}/private.h
  ${EXTERNAL}/qdldl/qdldl.c
  ${${PROJECT_NAME}_AMD_EXTERNAL_SRC}
  ${${PROJECT_NAME}_AMD_EXTERNAL_HDR}
  ${${PROJECT_NAME}_LDL_EXTERNAL_HDR})

target_include_directories(${${PROJECT_NAME}_DIRECT} PRIVATE
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${EXTERNAL}/amd;${CMAKE_CURRENT_SOURCE_DIR}/${EXTERNAL}/qdldl;${CMAKE_CURRENT_SOURCE_DIR}/${DIRSRC};${CMAKE_CURRENT_SOURCE_DIR}/${LINSYS}>")

target_include_directories(${${PROJECT_NAME}_DIRECT} PUBLIC
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
  "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/scs>")

# Compiled with blas and lapack, can solve LPs, SOCPs, SDPs, ECPs, and PCPs
target_compile_definitions(${${PROJECT_NAME}_DIRECT} PRIVATE -DUSE_LAPACK -DCOPYAMATRIX -DCTRLC)

# The library depends on math (m) blas and lapack
target_link_libraries(${${PROJECT_NAME}_DIRECT} PRIVATE
  m
  blas
  lapack)

# Set some properties
set_target_properties(${${PROJECT_NAME}_DIRECT} PROPERTIES
  VERSION ${scs_VERSION}
  PUBLIC_HEADER "${${PROJECT_NAME}_HDR}")

add_library(scs::${${PROJECT_NAME}_DIRECT} ALIAS ${${PROJECT_NAME}_DIRECT})

# Install the library
install(TARGETS ${${PROJECT_NAME}_DIRECT}
  EXPORT  ${PROJECT_NAME}
  COMPONENT runtime
  LIBRARY          DESTINATION "${CMAKE_INSTALL_LIBDIR}"                            COMPONENT shlib
  ARCHIVE          DESTINATION "${CMAKE_INSTALL_LIBDIR}"                            COMPONENT lib
  RUNTIME          DESTINATION "${CMAKE_INSTALL_BINDIR}"                            COMPONENT bin
  PUBLIC_HEADER    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/scs")

# Add the direct method to the set of the compiled targets
set_property(GLOBAL APPEND PROPERTY scs_TARGETS ${${PROJECT_NAME}_DIRECT})

### Indirect method
# Here we compile the indirect method library
set(${PROJECT_NAME}_INDIRECT ${PROJECT_NAME}indir)
add_library(${${PROJECT_NAME}_INDIRECT}
  ${${PROJECT_NAME}_HDR}
  ${${PROJECT_NAME}_SRC}
  ${INDIRSRC}/private.c
  ${INDIRSRC}/private.h
  ${${${PROJECT_NAME}_INDIRECT}_HDR}
  )


target_include_directories(${${PROJECT_NAME}_INDIRECT} PRIVATE
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${EXTERNAL}/amd;${CMAKE_CURRENT_SOURCE_DIR}/${EXTERNAL}/qdldl;${CMAKE_CURRENT_SOURCE_DIR}/${INDIRSRC};${CMAKE_CURRENT_SOURCE_DIR}/${LINSYS}>")

target_include_directories(${${PROJECT_NAME}_INDIRECT} PUBLIC
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
  "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/scs>")

# Compiled with blas and lapack, can solve LPs, SOCPs, SDPs, ECPs, and PCPs
target_compile_definitions(${${PROJECT_NAME}_INDIRECT} PRIVATE -DUSE_LAPACK -DCOPYAMATRIX -DCTRLC -DINDIRECT)

# The library depends on math (m) blas and lapack
target_link_libraries(${${PROJECT_NAME}_INDIRECT} PUBLIC
  m
  blas
  lapack)

# Set some properties
set_target_properties(${${PROJECT_NAME}_INDIRECT} PROPERTIES
  VERSION ${scs_VERSION}
  PUBLIC_HEADER "${${${PROJECT_NAME}_INDIRECT}_HDR}")

add_library(scs::${${PROJECT_NAME}_INDIRECT} ALIAS ${${PROJECT_NAME}_INDIRECT})

# Install the library
install(TARGETS ${${PROJECT_NAME}_INDIRECT}
  EXPORT  ${PROJECT_NAME}
  COMPONENT runtime
  LIBRARY       DESTINATION "${CMAKE_INSTALL_LIBDIR}"                            COMPONENT shlib
  ARCHIVE       DESTINATION "${CMAKE_INSTALL_LIBDIR}"                            COMPONENT lib
  RUNTIME       DESTINATION "${CMAKE_INSTALL_BINDIR}"                            COMPONENT bin
  PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/scs")

# Add the indirect method to the set of the compiled targets
set_property(GLOBAL APPEND PROPERTY scs_TARGETS ${${PROJECT_NAME}_INDIRECT})

### Install the .cmake file.
# Thanks to thanks file it will be possible to link scs to consumer libraries
include(InstallBasicPackageFiles)
install_basic_package_files(${PROJECT_NAME}
  NAMESPACE scs::
  VERSION ${${PROJECT_NAME}_VERSION}
  TARGETS_PROPERTY scs_TARGETS
  COMPATIBILITY SameMajorVersion
  VARS_PREFIX ${PROJECT_NAME}
  NO_CHECK_REQUIRED_COMPONENTS_MACRO)

### Add the tests
if(BUILD_TESTING)
  add_executable(run_tests_direct  test/run_tests.c)
  target_link_libraries(run_tests_direct PRIVATE
    scs::scsdir)
  target_include_directories(run_tests_direct PRIVATE
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/test;${CMAKE_CURRENT_SOURCE_DIR}/include;${CMAKE_CURRENT_SOURCE_DIR}/${LINSYS}>")

  add_test(NAME run_tests_direct
    COMMAND run_tests_direct
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

  add_executable(run_tests_indirect  test/run_tests.c)
  target_link_libraries(run_tests_indirect PRIVATE
    scs::scsindir)
  target_include_directories(run_tests_indirect PRIVATE
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/test;${CMAKE_CURRENT_SOURCE_DIR}/include;${CMAKE_CURRENT_SOURCE_DIR}/${LINSYS}>")

  add_test(NAME run_tests_indirect
    COMMAND run_tests_indirect
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

endif()