# Copyright (C) 2013-2021 Vincent Forest (vaplv@free.fr)
#
# This CMake script is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This CMake script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this CMake script. If not, see <http://www.gnu.org/licenses/>.

cmake_minimum_required(VERSION 3.0)
project(rsys C)
enable_testing()

if(POLICY CMP0054)
  cmake_policy(SET CMP0054 NEW)
endif()

set(RSYS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src)
option(NO_TEST "Disable the test" OFF)

################################################################################
# Check dependencies
################################################################################
find_package(OpenMP)
find_package(RCMake REQUIRED)
find_package(Threads REQUIRED)

set(CMAKE_MODULE_PATH ${RCMAKE_SOURCE_DIR})
include(rcmake)

################################################################################
# Configure and define targets
################################################################################
set(VERSION_MAJOR 0)
set(VERSION_MINOR 12)
set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})

set(RSYS_FILES_SRC
  clock_time.c
  cstr.c
  hash.c
  image.c
  library.c
  logger.c
  mem_allocator.c
  mem_lifo_allocator.c
  mem_proxy_allocator.c
  quaternion.c
  str.c
  text_reader.c)

if(CMAKE_USE_PTHREADS_INIT)
  set(RSYS_FILES_SRC_THREAD
    pthread/pthread_condition.c
    pthread/pthread_mutex.c)
elseif(CMAKE_USE_WIN32_THREADS_INIT)
  set(RSYS_FILES_SRC_THREAD
    win32/win32_condition.c
    win32/win32_mutex.c)
endif()

set(RSYS_FILES_INC
  io_c99.h)
set(RSYS_FILES_INC_API
  algorithm.h
  binary_heap.h
  clock_time.h
  condition.h
  cstr.h
  double2.h
  double3.h
  double4.h
  double22.h
  double33.h
  double44.h
  dynamic_array.h
  dynamic_array_char.h
  dynamic_array_double.h
  dynamic_array_float.h
  dynamic_array_int.h
  dynamic_array_uchar.h
  dynamic_array_u32.h
  dynamic_array_u64.h
  dynamic_array_uint.h
  dynamic_array_size_t.h
  dynamic_array_str.h
  endianness.h
  float2.h
  float3.h
  float4.h
  float22.h
  float33.h
  float44.h
  free_list.h
  hash.h
  hash_table.h
  image.h
  library.h
  list.h
  logger.h
  math.h
  mem_allocator.h
  morton.h
  mutex.h
  quaternion.h
  real2.h
  real3.h
  realX.h
  realX_begin.h
  realX_end.h
  real22.h
  real33.h
  real44.h
  realXY.h
  realXY_begin.h
  realXY_end.h
  ref_count.h
  rsys.h
  signal.h
  str.h
  stretchy_array.h
  text_reader.h)

set(RSYS_FILES_DOC COPYING COPYING.LESSER README.md)

# Prepend each file in the `_files' list by `_path'
rcmake_prepend_path(RSYS_FILES_SRC ${RSYS_SOURCE_DIR})
rcmake_prepend_path(RSYS_FILES_SRC_THREAD ${RSYS_SOURCE_DIR})
rcmake_prepend_path(RSYS_FILES_INC ${RSYS_SOURCE_DIR})
rcmake_prepend_path(RSYS_FILES_INC_API ${RSYS_SOURCE_DIR})
rcmake_prepend_path(RSYS_FILES_DOC ${PROJECT_SOURCE_DIR}/../)

add_library(rsys SHARED
  ${RSYS_FILES_SRC}
  ${RSYS_FILES_SRC_THREAD}
  ${RSYS_FILES_INC}
  ${RSYS_FILES_INC_API})
set_target_properties(rsys
  PROPERTIES DEFINE_SYMBOL RSYS_SHARED_BUILD
  DEFINE_SYMBOL RSYS_SHARED_BUILD
  VERSION ${VERSION}
  SOVERSION ${VERSION_MAJOR})

target_link_libraries(rsys ${CMAKE_THREAD_LIBS_INIT})
if(CMAKE_COMPILER_IS_GNUCC)
  target_link_libraries(rsys m)
  if(NOT MINGW)
    target_link_libraries(rsys dl)
  endif()

  # On GLIBC version before 2.17 one has to link with the "rt" library to use
  # the clock_gettime function.
  find_library(_GLIBC NAMES glib-2.0)
  get_filename_component(_GLIB_LIBRARY_DIR ${_GLIBC} PATH)
  find_path(_GLIBCONFIG_INCLUDE_DIR NAMES glibconfig.h
    HINTS ${_GLIB_LIBRARY_DIR} PATH_SUFFIXES glib-2.0/include)

  if(_GLIBCONFIG_INCLUDE_DIR)
    file(STRINGS "${_GLIBCONFIG_INCLUDE_DIR}/glibconfig.h"
      _GLIB_MAJOR_LINE REGEX "^#define[ \t]+GLIB_MAJOR_VERSION[ \t]+[0-9]+$")
    file(STRINGS "${_GLIBCONFIG_INCLUDE_DIR}/glibconfig.h"
      _GLIB_MINOR_LINE REGEX "^#define[ \t]+GLIB_MINOR_VERSION[ \t]+[0-9]+$")
    file(STRINGS "${_GLIBCONFIG_INCLUDE_DIR}/glibconfig.h"
      _GLIB_PATCH_LINE REGEX "^#define[ \t]+GLIB_MICRO_VERSION[ \t]+[0-9]+$")
    string(REGEX REPLACE "^#define[ \t]+GLIB_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1"
      _GLIB_MAJOR "${_GLIB_MAJOR_LINE}")
    string(REGEX REPLACE "^#define[ \t]+GLIB_MINOR_VERSION[ \t]+([0-9]+)$" "\\1"
      _GLIB_MINOR "${_GLIB_MINOR_LINE}")
    string(REGEX REPLACE "^#define[ \t]+GLIB_MICRO_VERSION[ \t]+([0-9]+)$" "\\1"
      _GLIB_PATCH "${_GLIB_PATCH_LINE}")
    set(_GLIB_VERSION "${_GLIB_MAJOR}.${_GLIB_MINOR}.${_GLIB_PATCH}")
    if("${_GLIBC_VERSION}" VERSION_LESS "2.17")
      target_link_libraries(rsys rt)
    endif()
  endif()

endif()

rcmake_setup_devel(rsys RSys ${VERSION} rsys/rsys_version.h)

################################################################################
# Add tests
################################################################################
if(NOT NO_TEST)
  macro(new_test _name)
    add_executable(${_name} ${RSYS_SOURCE_DIR}/${_name}.c)
    set(_libraries  ${ARGN})
    foreach(_lib ${_libraries})
      target_link_libraries(${_name} ${_lib})
    endforeach()
    add_test(${_name} ${_name})
  endmacro()

  if(CMAKE_COMPILER_IS_GNUCC)
    set(MATH_LIB m)
  endif()

  new_test(test_algorithm)
  new_test(test_atomic)
  new_test(test_binary_heap rsys)
  new_test(test_cstr rsys)
  new_test(test_double2 ${MATH_LIB})
  new_test(test_double3 ${MATH_LIB})
  new_test(test_double4 ${MATH_LIB})
  new_test(test_double22)
  new_test(test_double33 ${MATH_LIB})
  new_test(test_double44)
  new_test(test_endianness)
  new_test(test_dynamic_array rsys)
  new_test(test_float2 ${MATH_LIB})
  new_test(test_float3 ${MATH_LIB})
  new_test(test_float4 ${MATH_LIB})
  new_test(test_float22 ${MATH_LIB})
  new_test(test_float33 ${MATH_LIB})
  new_test(test_float44)
  new_test(test_free_list rsys)
  new_test(test_func_name)
  new_test(test_hash_table rsys)
  new_test(test_hash_sha256 rsys)
  new_test(test_image rsys)
  new_test(test_library rsys)
  new_test(test_list rsys)
  new_test(test_logger rsys)
  new_test(test_math ${MATH_LIB})
  new_test(test_mem_allocator rsys)
  new_test(test_misc)
  new_test(test_morton)
  new_test(test_quaternion rsys)
  new_test(test_ref)
  new_test(test_signal rsys)
  new_test(test_str rsys)
  new_test(test_stretchy_array rsys)
  new_test(test_text_reader rsys)
  new_test(test_time rsys)
  new_test(test_vmacros)

  add_library(test_lib SHARED ${RSYS_SOURCE_DIR}/test_library.c)
  set_target_properties(test_lib PROPERTIES
    COMPILE_DEFINITIONS TEST_LIBRARY_BUILD_LIB
    DEBUG_POSTFIX "")

  if(CMAKE_COMPILER_IS_GNUCC)
    set_target_properties(test_cstr PROPERTIES COMPILE_FLAGS "-std=c99")

    # Remove a false positive warning
    if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.4
    OR CMAKE_C_COMPILER_VERSION VERSION_EQUAL 7.3)
      set_target_properties(test_binary_heap PROPERTIES COMPILE_FLAGS
        "-Wno-aggressive-loop-optimizations")
    endif()
  endif()

  if(NOT OPENMP_FOUND)
    message(STATUS "No OpenMP support: multi-threaded tests cannot be generated")
  else()
    new_test(test_mutex rsys)
    new_test(test_condition rsys)

    set_target_properties(test_mutex test_condition PROPERTIES
      COMPILE_FLAGS ${OpenMP_C_FLAGS})

    if(CMAKE_COMPILER_IS_GNUCC)
      set_target_properties(test_mutex test_condition PROPERTIES
        LINK_FLAGS ${OpenMP_C_FLAGS})
    endif()
  endif()
endif()

################################################################################
# Define output & install directories
################################################################################
install(TARGETS rsys
  ARCHIVE DESTINATION bin
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin)
install(FILES ${RSYS_FILES_INC_API} DESTINATION include/rsys)
install(FILES ${RSYS_FILES_DOC} DESTINATION share/doc/rsys)
