# Copyright (C) 2012-2018  Brazil
# Copyright (C) 2019-2026  Sutou Kouhei <kou@clear-code.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/c_sources.am LIBGROONGA_C_SOURCES)
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/cpp_sources.am
               LIBGROONGA_CPP_SOURCES)
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/dat/sources.am LIBGRNDAT_SOURCES)
string(REGEX REPLACE "([^;]+)" "dat/\\1" LIBGRNDAT_SOURCES
                     "${LIBGRNDAT_SOURCES}")
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/sources.am LIBGRNMRB_SOURCES)
string(REGEX REPLACE "([^;]+)" "mrb/\\1" LIBGRNMRB_SOURCES
                     "${LIBGRNMRB_SOURCES}")
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/cpp_sources.am
               LIBGRNMRB_CPP_SOURCES)
string(REGEX REPLACE "([^;]+)" "mrb/\\1" LIBGRNMRB_CPP_SOURCES
                     "${LIBGRNMRB_CPP_SOURCES}")
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/proc/c_sources.am
               LIBGRNPROC_C_SOURCES)
string(REGEX REPLACE "([^;]+)" "proc/\\1" LIBGRNPROC_C_SOURCES
                     "${LIBGRNPROC_C_SOURCES}")
read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/proc/cpp_sources.am
               LIBGRNPROC_CPP_SOURCES)
string(REGEX REPLACE "([^;]+)" "proc/\\1" LIBGRNPROC_CPP_SOURCES
                     "${LIBGRNPROC_CPP_SOURCES}")

if(WIN32)
  configure_file("metadata.rc.in" "${CMAKE_CURRENT_BINARY_DIR}/metadata.rc"
                 @ONLY)
  set(LIBGROONGA_METADATA_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/metadata.rc")
else()
  set(LIBGROONGA_METADATA_SOURCES)
endif()

set_source_files_properties(
  ${LIBGROONGA_C_SOURCES} ${LIBGRNMRB_SOURCES} ${LIBGRNPROC_C_SOURCES}
  PROPERTIES COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
set_source_files_properties(
  ${LIBGROONGA_CPP_SOURCES} ${LIBGRNDAT_SOURCES} ${LIBGRNMRB_CPP_SOURCES}
  ${LIBGRNPROC_CPP_SOURCES} PROPERTIES COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS}")

if(GRN_C_COMPILER_GNU_LIKE)
  set_property(
    SOURCE distance.cpp
    APPEND_STRING
    PROPERTY COMPILE_FLAGS " -Wno-unknown-pragmas -Wno-float-equal")
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
  # TODO: Remove this when OpenZL fixes duplicate typedef definitions
  # in their headers.
  # ref: https://github.com/facebook/openzl/pull/293
  set_property(
    SOURCE compressor.c
    APPEND_STRING
    PROPERTY COMPILE_FLAGS " -Wno-typedef-redefinition")
endif()
if(GRN_WITH_XSIMD)
  if(GRN_WITH_SIMD_AVX)
    list(APPEND LIBGROONGA_CPP_SOURCES distance_avx.cpp)
    set_property(
      SOURCE distance_avx.cpp
      APPEND_STRING
      PROPERTY COMPILE_FLAGS " ${GRN_C_FLAGS_AVX}")
  endif()
  if(GRN_WITH_SIMD_AVX2)
    list(APPEND LIBGROONGA_CPP_SOURCES distance_avx2.cpp)
    set_property(
      SOURCE distance_avx2.cpp
      APPEND_STRING
      PROPERTY COMPILE_FLAGS " ${GRN_C_FLAGS_AVX2}")
  endif()
  if(GRN_WITH_SIMD_AVX512)
    list(APPEND LIBGROONGA_CPP_SOURCES distance_avx512.cpp)
    set_property(
      SOURCE distance_avx512.cpp
      APPEND_STRING
      PROPERTY COMPILE_FLAGS " ${GRN_C_FLAGS_AVX512}")
  endif()
  if(GRN_WITH_SIMD_NEON64)
    list(APPEND LIBGROONGA_CPP_SOURCES distance_neon64.cpp)
    set_property(
      SOURCE distance_neon64.cpp
      APPEND_STRING
      PROPERTY COMPILE_FLAGS " ${GRN_C_FLAGS_NEON64}")
  endif()
  list(APPEND LIBGROONGA_CPP_SOURCES distance_common.cpp)
  if(GRN_C_COMPILER_GNU_LIKE)
    set_property(
      SOURCE distance_common.cpp
      APPEND_STRING
      PROPERTY COMPILE_FLAGS " -Wno-float-equal")
  endif()
endif()

set(GRN_ALL_SOURCES
    ${LIBGROONGA_C_SOURCES}
    ${LIBGROONGA_CPP_SOURCES}
    ${LIBGRNDAT_SOURCES}
    ${LIBGRNMRB_SOURCES}
    ${LIBGRNMRB_CPP_SOURCES}
    ${LIBGRNPROC_C_SOURCES}
    ${LIBGRNPROC_CPP_SOURCES}
    ${LIBGROONGA_METADATA_SOURCES})
if(WASI)
  # WASI doesn't support dynamic linking. libgroonga.wasm is a
  # WebAssembly module that exports the C API.
  add_executable(libgroonga ${GRN_ALL_SOURCES})
  set_target_properties(libgroonga PROPERTIES PREFIX "lib" SUFFIX ".wasm")
  target_link_options(
    libgroonga
    PRIVATE
    # libgroonga.wasm is a library. So it doesn't have main().
    "-mexec-model=reactor"
    # Export all symbols that have the default visibility. See GRN_API
    # in lib/grn.h.
    "-Wl,--export-dynamic"
    # A user of libgroonga.wasm needs them to pass a string to the C
    # API and to use a callback of the C API.
    "-Wl,--export=malloc"
    "-Wl,--export=free"
    "-Wl,--export-table"
    "-Wl,--growable-table"
    # The default stack size of wasm-ld is only 64KiB. Stack overflow
    # corrupts static data silently because the stack is placed after
    # static data by default. Use 8MiB like the main thread on Linux
    # and place the stack first to detect stack overflow as a trap.
    "-Wl,--stack-first"
    "-Wl,-z,stack-size=8388608")
elseif(GRN_EMBED)
  add_library(libgroonga STATIC ${GRN_ALL_SOURCES})
  target_compile_definitions(libgroonga PUBLIC GRN_STATIC)
  target_compile_definitions(libgroonga PUBLIC GRN_EMBEDDED)
  set_target_properties(libgroonga PROPERTIES POSITION_INDEPENDENT_CODE ON)
else()
  add_library(libgroonga SHARED ${GRN_ALL_SOURCES})
  target_compile_definitions(libgroonga PRIVATE GRN_DAT_EXPORT)
endif()
if(WASI)
  add_executable(libgroonga_callback wasm_callback.c)
  set_target_properties(
    libgroonga_callback PROPERTIES OUTPUT_NAME "libgroonga-callback" SUFFIX
                                                                     ".wasm")
  target_link_options(
    libgroonga_callback
    PRIVATE
    # This module only forwards a call. So it doesn't have main() and
    # doesn't need libc.
    "-mexec-model=reactor"
    "-nostdlib"
    "-Wl,--no-entry"
    "-Wl,--strip-all")
  install(TARGETS libgroonga_callback DESTINATION "${CMAKE_INSTALL_LIBDIR}")
  install(FILES libgroonga.mjs libgroonga-node.mjs libgroonga-wasi.mjs
                libgroonga-web.mjs DESTINATION "${CMAKE_INSTALL_LIBDIR}")
endif()
if(GRN_WITH_LTO AND CFLAG_WERROR_ODR)
  target_link_options(libgroonga PRIVATE -Werror=odr)
endif()
target_compile_definitions(libgroonga PUBLIC "$<BUILD_INTERFACE:HAVE_CONFIG_H>")
target_include_directories(libgroonga BEFORE
                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/dat)
target_include_directories(
  libgroonga BEFORE
  PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/..>"
         "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>"
         "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>"
         "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
if(NOT MSVC)
  set_target_properties(libgroonga PROPERTIES OUTPUT_NAME "groonga")
endif()
if(NOT WASI)
  set_target_properties(libgroonga PROPERTIES VERSION "0.0.0" SOVERSION "0")
endif()

# We require Lemon (SQLite) 3.52.0 or later but Lemon doesn't provide
# a way to get version information. ("lemon -x" always prints "Lemon
# version 1.0") So we can't detect Lemon version automatically.
if(GRN_USE_LEMON)
  find_program(LEMON lemon REQUIRED)
  set_property(
    SOURCE expr.c
    PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grn_ecmascript.c"
             "${CMAKE_CURRENT_SOURCE_DIR}/grn_ecmascript.h")
  add_custom_command(
    OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/grn_ecmascript.c"
           "${CMAKE_CURRENT_SOURCE_DIR}/grn_ecmascript.h"
    COMMAND ${LEMON} grn_ecmascript.lemon
    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grn_ecmascript.lemon"
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    VERBATIM)
endif()

if(GRN_EMBED)
  set(GRN_EMBEDDED_PLUGIN_LIBRARIES "")
  if(GRN_WITH_MECAB)
    list(APPEND GRN_EMBEDDED_PLUGIN_LIBRARIES mecab_tokenizer)
  endif()
  target_link_libraries(libgroonga PRIVATE grn_dependencies
                                           ${GRN_EMBEDDED_PLUGIN_LIBRARIES})
else()
  target_link_libraries(libgroonga PRIVATE grn_dependencies)
  target_include_directories(
    libgroonga
    INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/groonga>")
  if(WASI)
    # Exporting libgroonga.wasm requires exporting grn_dependencies
    # too. We don't provide the CMake package for WASI for now.
    install(TARGETS libgroonga RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}")
  else()
    install(
      TARGETS libgroonga
      EXPORT libgroonga_export
      ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
    set(CONFIG_CMAKE "GroongaConfig.cmake")
    set(BUILT_CONFIG_CMAKE "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_CMAKE}")
    configure_package_config_file(
      "${CONFIG_CMAKE}.in" "${BUILT_CONFIG_CMAKE}"
      INSTALL_DESTINATION "${GRN_INSTALL_CMAKE_DIR}"
      PATH_VARS GRN_RELATIVE_PLUGINS_DIR)
    set(CONFIG_VERSION_CMAKE "GroongaConfigVersion.cmake")
    set(BUILT_CONFIG_VERSION_CMAKE
        "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_VERSION_CMAKE}")
    write_basic_package_version_file("${BUILT_CONFIG_VERSION_CMAKE}"
                                     COMPATIBILITY AnyNewerVersion)
    install(FILES "${BUILT_CONFIG_CMAKE}" "${BUILT_CONFIG_VERSION_CMAKE}"
            DESTINATION "${GRN_INSTALL_CMAKE_DIR}")
    install(
      EXPORT libgroonga_export
      DESTINATION "${GRN_INSTALL_CMAKE_DIR}"
      NAMESPACE "Groonga::"
      FILE "GroongaTargets.cmake")
    if(MSVC)
      install(
        FILES $<TARGET_PDB_FILE:libgroonga>
        DESTINATION "${CMAKE_INSTALL_BINDIR}"
        OPTIONAL)
    endif()
  endif()
endif()
if(GRN_WITH_MESSAGE_PACK)
  if(GRN_WITH_BUNDLED_MESSAGE_PACK)
    target_link_libraries(libgroonga
                          INTERFACE "$<BUILD_INTERFACE:Groonga::msgpackc>")
  else()
    target_link_libraries(libgroonga INTERFACE Groonga::msgpackc)
  endif()
endif()
if(GRN_WITH_LLAMA_CPP_BUNDLED)
  target_link_libraries(libgroonga
                        INTERFACE "$<BUILD_INTERFACE:Groonga::llama>")
  target_link_libraries(libgroonga INTERFACE "$<BUILD_INTERFACE:Groonga::ggml>")
  target_link_libraries(libgroonga
                        INTERFACE "$<BUILD_INTERFACE:Groonga::ggml-base>")
endif()
if(NOT WASI)
  target_link_libraries(libgroonga PRIVATE Threads::Threads)
endif()
# wasi-libc provides dlopen() in libdl.a too.
target_link_libraries(libgroonga PRIVATE ${CMAKE_DL_LIBS})
if(WIN32)
  target_link_libraries(libgroonga PRIVATE ws2_32.lib)
endif()

if(GRN_WITH_MRUBY)
  target_link_libraries(libgroonga INTERFACE "$<BUILD_INTERFACE:mruby_headers>")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/sources.am
                 RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/\\1" RUBY_SCRIPTS
                       "${RUBY_SCRIPTS}")
  install(FILES ${RUBY_SCRIPTS} DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/command_line/sources.am
                 COMMANE_LINE_RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/command_line/\\1"
                       COMMANE_LINE_RUBY_SCRIPTS "${COMMANE_LINE_RUBY_SCRIPTS}")
  install(FILES ${COMMANE_LINE_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/command_line")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/context/sources.am
                 CONTEXT_RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/context/\\1" CONTEXT_RUBY_SCRIPTS
                       "${CONTEXT_RUBY_SCRIPTS}")
  install(FILES ${CONTEXT_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/context")

  read_file_list(
    ${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/expression_rewriters/sources.am
    EXPRESSION_REWRITERS_RUBY_SCRIPTS)
  string(
    REGEX
    REPLACE "([^;]+)" "mrb/scripts/expression_rewriters/\\1"
            EXPRESSION_REWRITERS_RUBY_SCRIPTS
            "${EXPRESSION_REWRITERS_RUBY_SCRIPTS}")
  install(FILES ${EXPRESSION_REWRITERS_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/expression_rewriters")

  read_file_list(
    ${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/expression_tree/sources.am
    EXPRESSION_TREE_RUBY_SCRIPTS)
  string(REGEX
         REPLACE "([^;]+)" "mrb/scripts/expression_tree/\\1"
                 EXPRESSION_TREE_RUBY_SCRIPTS "${EXPRESSION_TREE_RUBY_SCRIPTS}")
  install(FILES ${EXPRESSION_TREE_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/expression_tree")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/initialize/sources.am
                 INITIALIZE_RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/initialize/\\1"
                       INITIALIZE_RUBY_SCRIPTS "${INITIALIZE_RUBY_SCRIPTS}")
  install(FILES ${INITIALIZE_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/initialize")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/logger/sources.am
                 LOGGER_RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/logger/\\1" LOGGER_RUBY_SCRIPTS
                       "${LOGGER_RUBY_SCRIPTS}")
  install(FILES ${LOGGER_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/logger")

  read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/query_logger/sources.am
                 QUERY_LOGGER_RUBY_SCRIPTS)
  string(REGEX REPLACE "([^;]+)" "mrb/scripts/query_logger/\\1"
                       QUERY_LOGGER_RUBY_SCRIPTS "${QUERY_LOGGER_RUBY_SCRIPTS}")
  install(FILES ${QUERY_LOGGER_RUBY_SCRIPTS}
          DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}/query_logger")
endif()
