cmake_minimum_required(VERSION 3.10)

# project specification

project(nimisewi
  VERSION 0.3.1
  DESCRIPTION "small program that generates random toki pona noun phrase"
  HOMEPAGE_URL "https://fsl.aaoth.xyz/nimisewi.c/home")

# get compiler name and version

execute_process(COMMAND ${CMAKE_C_COMPILER} "--version"
    OUTPUT_VARIABLE _PROJECT_C_COMPILER_VERSION)
string(REGEX REPLACE "\n" ";"
    PROJECT_C_COMPILER_VERSION ${_PROJECT_C_COMPILER_VERSION})
list(APPEND PROJECT_C_COMPILER_STRING ${PROJECT_C_COMPILER_VERSION})
list(POP_FRONT PROJECT_C_COMPILER_STRING PROJECT_C_COMPILER_VERSION)

# configuration header

configure_file(config.h.in config.h)

# main executable

add_executable(nimisewi main.c nimisewi.c
  ${PROJECT_BINARY_DIR}/nimitoki.c)
target_include_directories(nimisewi PUBLIC "${PROJECT_BINARY_DIR}")

# supplementary executable

add_executable(makenimitoki makenimitoki.c)
target_include_directories(makenimitoki PUBLIC "${PROJECT_BINARY_DIR}")

add_custom_command(
  OUTPUT ${PROJECT_BINARY_DIR}/nimitoki.c
  ${PROJECT_BINARY_DIR}/nimitoki.h
  COMMAND makenimitoki ${PROJECT_SOURCE_DIR}/nimitoki.txt
  DEPENDS makenimitoki)

# includes

include(CheckIncludeFile)
include(CheckSymbolExists)
include(CheckCSourceCompiles)

# warning message

set(WARNING_STATIC_BUILD "static build won't work here")

# check libraries

cmake_host_system_information(RESULT OS_NAME QUERY OS_NAME)
message(VERBOSE "OS_NAME is ${OS_NAME}")
if(OS_NAME MATCHES "Linux")
  check_include_file("bsd/string.h" HAVE_BSD_STRING_H)
  check_include_file("bsd/stdlib.h" HAVE_BSD_STDLIB_H)
  if(HAVE_BSD_STRING_H AND HAVE_BSD_STDLIB_H)
    set(CMAKE_REQUIRED_LIBRARIES "bsd")

    check_symbol_exists(strlcat "bsd/string.h" HAVE_STRLCAT)
    if (HAVE_STRLCAT)
      list(APPEND EXTRA_DEFINITIONS HAVE_BSD_STRING_H HAVE_STRLCAT)
    endif()

    check_symbol_exists(arc4random_uniform "bsd/stdlib.h"
      HAVE_ARC4RANDOM_UNIFORM)
    if (HAVE_ARC4RANDOM_UNIFORM)
      list(APPEND EXTRA_DEFINITIONS HAVE_BSD_STDLIB_H HAVE_ARC4RANDOM_UNIFORM)
    endif()

    # check static libraries
    set(CMAKE_REQUIRED_LINK_OPTIONS "-static")
    check_c_source_compiles(
      "#include<bsd/string.h>\nint main(void){return 0;}"
      HAVE_LIBBSD_STATIC)

    if(NOT ANDROID)
      # libpthread and libmd are needed when linking statically on linux.
      # these test aren't really checking particular functions, but
      # they are here to make sure that program could be linked statically
      unset(CMAKE_REQUIRED_LIBRARIES)
      set(CMAKE_REQUIRED_LIBRARIES "pthread")
      check_c_source_compiles(
        "#include<pthread.h>\nint main(void){return 0;}"
        HAVE_LIBPTHREAD_STATIC)

      unset(CMAKE_REQUIRED_LIBRARIES)
      set(CMAKE_REQUIRED_LIBRARIES "md")
      check_c_source_compiles(
        "#include<sha2.h>\nint main(void){return 0;}"
        HAVE_LIBMD_STATIC)

      if(NOT (HAVE_LIBBSD_STATIC
            AND HAVE_LIBPTHREAD_STATIC
            AND HAVE_LIBMD_STATIC))
        message(WARNING ${WARNING_STATIC_BUILD})
        list(APPEND EXTRA_LIBS bsd pthread md)
      endif()

    else()
      if(NOT (HAVE_LIBBSD_STATIC))
        message(WARNING ${WARNING_STATIC_BUILD})
      endif()
      list(APPEND EXTRA_LIBS bsd)
    endif()

    unset(CMAKE_REQUIRED_LIBRARIES)
    unset(CMAKE_REQUIRED_LINK_OPTIONS)
  endif()
endif()

if(OS_NAME MATCHES "(.*BSD)|(DragonFly)")
  check_symbol_exists(strlcat "string.h" HAVE_STRLCAT)
  if(HAVE_STRLCAT)
    list(APPEND EXTRA_DEFINITIONS HAVE_STRLCAT)
  endif()
  check_symbol_exists(arc4random_uniform "stdlib.h" HAVE_ARC4RANDOM_UNIFORM)
  if(HAVE_ARC4RANDOM_UNIFORM)
    list(APPEND EXTRA_DEFINITIONS HAVE_ARC4RANDOM_UNIFORM)
  endif()
  check_symbol_exists(pledge "unistd.h" HAVE_PLEDGE)
  if(HAVE_PLEDGE)
    list(APPEND EXTRA_DEFINITIONS HAVE_PLEDGE)
  endif()
endif()

set(CMAKE_REQUIRED_LINK_OPTIONS "-static")
check_c_source_compiles(
  "#include<stdio.h>\nint main(void){return 0;}" HAVE_LIBC_STATIC
  )
if(NOT HAVE_LIBC_STATIC)
  message(WARNING ${WARNING_STATIC_BUILD})
endif()

target_link_libraries(nimisewi PUBLIC ${EXTRA_LIBS})
target_compile_definitions(nimisewi PRIVATE ${EXTRA_DEFINITIONS})

set(NIMISEWI_CGI_STATIC_BUILD FALSE)
if(HAVE_LIBC_STATIC)
  if(HAVE_BSD_STRING_H AND HAVE_BSD_STDLIB_H)
    if(HAVE_LIBBSD_STATIC)
      set(NIMISEWI_CGI_STATIC_BUILD TRUE)
    endif()
  else()
    set(NIMISEWI_CGI_STATIC_BUILD TRUE)
  endif()
endif()

# optional binaries

option(BUILD_CGI "build cgi version of nimisewi" OFF)
if(BUILD_CGI)
  add_executable(nimisewi.cgi cgi.c nimisewi.c
    ${PROJECT_BINARY_DIR}/nimitoki.c)
  option(CGI_INSTALL_DIR "specify installation directory of cgi script")
  if(CGI_INSTALL_DIR)
    set_property(TARGET nimisewi.cgi PROPERTY CGI_INSTALL_DIR
      ${CGI_INSTALL_DIR})
  else()
    set_property(TARGET nimisewi.cgi PROPERTY CGI_INSTALL_DIR
      "var/www/htdocs/cgi-bin")
  endif()
  target_include_directories(nimisewi.cgi PUBLIC "${PROJECT_BINARY_DIR}")
  target_compile_definitions(nimisewi.cgi PRIVATE ${EXTRA_DEFINITIONS})
  target_link_libraries(nimisewi.cgi PUBLIC ${EXTRA_LIBS})
  if(NIMISEWI_CGI_STATIC_BUILD)
    target_link_options(nimisewi.cgi PUBLIC -static)
  endif()
endif()

option(BUILD_GEMINI_CGI "build cgi version of nimisewi for gemini protocol" OFF)
if(BUILD_GEMINI_CGI)
  add_executable(nimisewi_gemini.cgi cgi_gemini.c nimisewi.c
    ${PROJECT_BINARY_DIR}/nimitoki.c)
  option(GEMINI_CGI_INSTALL_DIR "specify installation directory of cgi script")
  if(GEMINI_CGI_INSTALL_DIR)
    set_property(TARGET nimisewi_gemini.cgi PROPERTY GEMINI_CGI_INSTALL_DIR
      ${GEMINI_CGI_INSTALL_DIR})
  else()
    set_property(TARGET nimisewi_gemini.cgi PROPERTY GEMINI_CGI_INSTALL_DIR
      "var/gemini/cgi-bin")
  endif()
  target_include_directories(nimisewi_gemini.cgi PUBLIC "${PROJECT_BINARY_DIR}")
  target_compile_definitions(nimisewi_gemini.cgi PRIVATE ${EXTRA_DEFINITIONS})
  target_link_libraries(nimisewi_gemini.cgi PUBLIC ${EXTRA_LIBS})
  if(NIMISEWI_CGI_STATIC_BUILD)
    target_link_options(nimisewi_gemini.cgi PUBLIC -static)
  endif()
endif()

# installation

install(TARGETS nimisewi DESTINATION bin)
if(BUILD_CGI)
  install(TARGETS nimisewi.cgi
    DESTINATION $<TARGET_PROPERTY:nimisewi.cgi,CGI_INSTALL_DIR>)
endif()
if(BUILD_GEMINI_CGI)
  install(TARGETS nimisewi_gemini.cgi
    DESTINATION $<TARGET_PROPERTY:nimisewi_gemini.cgi,GEMINI_CGI_INSTALL_DIR>)
endif()

# packaging

include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PROJECT_VERSION_MAJOR ${nimisewi_VERSION_MAJOR})
set(CPACK_PROJECT_VERSION_MINOR ${nimisewi_VERSION_MINOR})
set(CPACK_PROJECT_VERSION_PATCH ${nimisewi_VERSION_PATCH})
set(CPACK_PACKAGE_FILE_NAME
  ${CMAKE_PROJECT_NAME}-${nimisewi_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
set(CPACK_GENERATOR "TGZ" "ZIP")
set(CPACK_SOURCE_GENERATOR "TGZ" "ZIP")
set(CPACK_SOURCE_IGNORE_FILES
  \.fslckout
  \.fossil-settings
  build/
  utils/
  download.md
  )
set(CPACK_STRIP_FILES TRUE)
include(CPack)

# testing

enable_testing()

add_test(NAME runs COMMAND nimisewi)

add_test(NAME version COMMAND nimisewi -v)
set_tests_properties(version
  PROPERTIES PASS_REGULAR_EXPRESSION
  "nimisewi [0-9]\.[0-9]\.[0-9]\n[a-z ]+(\nfeatures:\n([A-Z_]+(\n)?)+)?")

if(BUILD_CGI)
  add_test(NAME cgi COMMAND nimisewi.cgi)
  set_tests_properties(cgi
    PROPERTIES PASS_REGULAR_EXPRESSION
    "Status: 200 OK\rContent-Type: text\/html\r\r<.*>")
endif()
if(BUILD_GEMINI_CGI)
  add_test(NAME gemini_cgi COMMAND nimisewi_gemini.cgi)
  set_tests_properties(gemini_cgi
    PROPERTIES PASS_REGULAR_EXPRESSION "20 text\/gemini\r\n.*")
endif()