cmake_minimum_required(VERSION 3.11)

# FindBoost is removed in version cmake 3.30
if(POLICY CMP0167)
    cmake_policy(SET CMP0167 NEW)
endif()

set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_DEBUG} ${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_RELEASE}")

project(SOPLEX
  LANGUAGES CXX)

if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")
   # require at least gcc 5
   if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
     message(WARNING "GCC version not supported, should be at least 5!")
   endif()
endif()

set(SOPLEX_VERSION_MAJOR 8)
set(SOPLEX_VERSION_MINOR 0)
set(SOPLEX_VERSION_PATCH 2)
set(SOPLEX_VERSION_API 20)

set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "${SOPLEX_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${SOPLEX_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${SOPLEX_VERSION_PATCH}")
set(CPACK_PACKAGE_VENDOR "Zuse Institute Berlin")
include(CPack)

option(ZLIB "Use ZLIB" ON)
option(GMP "Use GMP" ON)
option(EMSCRIPTEN_HTML "Emscripten HTML output" OFF)
option(STATIC_GMP "Prefer static GMP lib" OFF)
option(BOOST "Use Boost (required to build the binary). Disable if you only want to build libsoplex." ON)
option(QUADMATH "should quadmath library be used" OFF)
option(MPFR "Use MPFR" ON)
option(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS "Export all symbols into the DLL" ON)
option(MT "use static runtime libraries for Visual Studio compiler" OFF)
option(SANITIZE "should sanitizers be enabled if available" OFF)
option(COVERAGE "enable coverage support" OFF)
option(PAPILO "should papilo library be linked" ON)

SET(COVERAGE_CTEST_ARGS "" CACHE STRING "additional ctest arguments for coverage")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# for colorized output
if(NOT WIN32)
  string(ASCII 27 Esc)
  set(ColourReset "${Esc}[m")
  set(Red         "${Esc}[31m")
endif()

# path to e.g. findGMP module
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)

# make 'Release' the default build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

# set the correct rpath for OS X
set(CMAKE_MACOSX_RPATH ON)

# use C++14 standard
set(CMAKE_CXX_STANDARD 14)

# set function visibility default to hidden
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)

# create a target for updating the current git hash
file(WRITE ${CMAKE_BINARY_DIR}/soplex_update_githash.cmake "
find_program(GIT git)
if(EXISTS \${DST})
   file(STRINGS \${DST} GITHASH_OLD)
   string(REGEX REPLACE \"#define SPX_GITHASH \\\"(.*)\\\"\" \"\\\\1\" GITHASH_OLD \${GITHASH_OLD})
endif()
if((GIT) AND (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git))
   execute_process(
      COMMAND \${GIT} describe --always --dirty
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
      OUTPUT_VARIABLE GITHASH OUTPUT_STRIP_TRAILING_WHITESPACE)
   string(REGEX REPLACE \"^.*-g\" \"\" GITHASH \${GITHASH})
   if(NOT \${GITHASH} STREQUAL \"\${GITHASH_OLD}\")
      file(WRITE \${DST} \"#define SPX_GITHASH \\\"\${GITHASH}\\\"\n\")
   endif()
else()
   set(GITHASH \${GITHASH_OLD})
   if(NOT GITHASH)
      message(STATUS \"Compiling without git information\")
      set(GITHASH \"NoGitInfo\")
   endif()
   file(WRITE \${DST} \"#define SPX_GITHASH \\\"\${GITHASH}\\\"\n\")
endif()
message(STATUS \"Git hash: \" \${GITHASH})
")
add_custom_target(soplex_update_githash
                  COMMAND ${CMAKE_COMMAND} -DDST=${PROJECT_SOURCE_DIR}/src/soplex/git_hash.cpp
                                           -P ${CMAKE_BINARY_DIR}/soplex_update_githash.cmake)

# write config.h that might have been generated by make
add_custom_target(soplex_cleanup_config ALL
                  COMMAND ${CMAKE_COMMAND} -E remove -f "${PROJECT_SOURCE_DIR}/src/soplex/config.h")

# include build before to ensure local configuration
include_directories(BEFORE ${PROJECT_BINARY_DIR})

if(NOT "${CMAKE_CXX_COMPILE_FEATURES}" MATCHES "cxx_thread_local")
    add_definitions(-DTHREADLOCAL=)
endif()

# enable coverage support
if(COVERAGE)
    include(CodeCoverage)
    APPEND_COVERAGE_COMPILER_FLAGS()
    set(COVERAGE_EXCLUDES '/usr*')
    SETUP_TARGET_FOR_COVERAGE(NAME coverage
                          EXECUTABLE bash -c "ctest ${COVERAGE_CTEST_ARGS}"
                          DEPENDENCIES soplex)
endif()

if(ZLIB)
    find_package(ZLIB)
endif()
if(ZLIB_FOUND)
    set(SOPLEX_WITH_ZLIB on)
    set(libs ${libs} ${ZLIB_LIBRARIES})
    include_directories(${ZLIB_INCLUDE_DIRS})
endif()

if(GMP)
    find_package(GMP)
endif()
if(GMP_FOUND)
    set(SOPLEX_WITH_GMP on)
    include_directories(${GMP_INCLUDE_DIRS})
    set(libs ${libs} ${GMP_LIBRARIES})
endif()

if(QUADMATH)
   find_package(Quadmath)
endif()
if(Quadmath_FOUND)
   set(SOPLEX_WITH_FLOAT128 on)
   include_directories(${Quadmath_INCLUDE_DIRS})
   set(libs ${libs} ${Quadmath_LIBRARY})
else()
   set(QUADMATH_IMPORTED_TARGET "")
endif()

# look for presolvelib PaPILO
if(PAPILO)
    message(STATUS "Finding PAPILO")
    find_package(PAPILO CONFIG HINTS ${PAPILO_DIR})
    set(PAPILO_MIN_VERSION 2.0.0)
    if(PAPILO_VERSION LESS ${PAPILO_MIN_VERSION})
        message( FATAL_ERROR "At least PaPILO Version ${PAPILO_MIN_VERSION} required - found ${PAPILO_VERSION}!")
    endif ()
endif()
if(PAPILO_FOUND)
    message(STATUS "Finding PAPILO - found")
    set(libs ${libs} ${PAPILO_IMPORTED_TARGETS})
    set(SOPLEX_WITH_PAPILO on)
else()
    message(STATUS "Support PAPILO: OFF")
    set(PAPILO_IMPORTED_TARGETS "")
    set(SOPLEX_WITH_PAPILO off)
endif()

set(BOOST_MINIMUM_VERSION 1.65.0)
if(BOOST)
    find_package(Boost ${BOOST_MINIMUM_VERSION}) # PaPILO requires at least Boost 1.65 (on mac 1.72)
    if(Boost_FOUND)
        set(SOPLEX_WITH_BOOST on)
        if(NOT Boost_VERSION_MACRO)
           set(Boost_VERSION_MACRO ${Boost_VERSION})
        endif()
        if(${Boost_VERSION_MACRO} LESS "107000")
            if(NOT SOPLEX_WITH_GMP)
               message(SEND_ERROR "If no GMP is linked, then the minimal Boost verion is 1.70. \
                       Found Boost version is ${Boost_VERSION_STRING}. Either provide newer Boost, link GMP, or disable Boost by setting BOOST=off.")
            else()
               message(WARNING "The multiprecision and quadprecision features are disabled with Boost versions older than 1.70. \
                       Found Boost version is ${Boost_VERSION_STRING}.")
            endif()
        endif()
        if(Boost_DIR)
            message(STATUS "Found Boost: ${Boost_DIR}")
        endif()
        set(libs ${libs} Boost::boost)
        if(MPFR) # MPFR is used within boost multiprecision, so using it without Boost does not make sense
            find_package(MPFR)
        endif()
        if(MPFR_FOUND)
            message(STATUS "SoPlex with Boost MPFR ${MPFR_VERSION} libraries")
            set(SOPLEX_WITH_MPFR on)
            include_directories(${MPFR_INCLUDE_DIRS})
            set(libs ${libs} ${MPFR_LIBRARIES})
        else()
            message(STATUS "SoPlex with Boost CPP multiprecision libraries")
            set(SOPLEX_WITH_CPPMPF on)
        endif()
    else()
        set(BOOST off)
    endif()
endif()

# disable fused floating point contraction to enhance reproducibility across compilers and architectures
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
    add_compile_options(/fp:precise /bigobj)
    add_definitions(/wd4100)
    add_definitions(/wd4661)
    add_definitions(-D_CRT_SECURE_NO_WARNINGS)
    add_definitions(-DBOOST_ALL_NO_LIB)
    # set MT or MTd flag when using the static VS runtime libraries
    if(MT)
        set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
        set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
    endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
    add_compile_options(-fp-model=precise)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
    # require at least gcc 5
    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
        message(WARNING "GCC version not supported, should be at least 5!")
    endif()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")
    add_compile_options(-ffp-contract=off)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    add_compile_options(-ffp-contract=off)
endif()

# enable all warnings and explicitly switch on colored output for ninja
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -Wall -Wno-strict-overflow")
    if(CMAKE_GENERATOR STREQUAL "Ninja")
        set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
    endif()
endif()

# export compilation settings to header file
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/soplex/config.h.in ${PROJECT_BINARY_DIR}/soplex/config.h @ONLY)

# pass on the CMake module path from the SoPlex source tree
set(EXTRA_CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)

configure_file(${PROJECT_SOURCE_DIR}/soplex-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/soplex-config.cmake" @ONLY)

add_subdirectory(src)
add_subdirectory(tests/c_interface)
add_subdirectory(check)

enable_testing()
