# 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()

get_property(
  GRN_EMBEDDED_PLUGIN_TARGETS
  TARGET grn_dependencies
  PROPERTY GRN_EMBEDDED_PLUGIN_TARGETS)
get_property(
  GRN_EMBEDDED_PLUGIN_NAMES
  TARGET grn_dependencies
  PROPERTY GRN_EMBEDDED_PLUGIN_NAMES)
set(GRN_EMBEDDED_PLUGIN_DECLARATIONS "")
set(GRN_EMBEDDED_PLUGIN_ENTRIES "")
foreach(name IN LISTS GRN_EMBEDDED_PLUGIN_NAMES)
  # The tag of a plugin is its name that replaces "/" with "_". See
  # GRN_PLUGIN_FUNCTION_TAG in the source of the plugin.
  string(REPLACE "/" "_" tag "${name}")
  string(APPEND GRN_EMBEDDED_PLUGIN_DECLARATIONS
         "GRN_PLUGIN_DECLARE_FUNCTIONS(${tag});\n")
  string(
    APPEND GRN_EMBEDDED_PLUGIN_ENTRIES "  {\"${name}\",\n"
    "   grn_plugin_impl_init_${tag},\n" "   grn_plugin_impl_register_${tag},\n"
    "   grn_plugin_impl_fin_${tag}},\n")
endforeach()
configure_file(embedded_plugins.c.in embedded_plugins.c @ONLY)
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_plugins.c"
                            PROPERTIES COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")

set(GRN_ALL_SOURCES
    "${CMAKE_CURRENT_BINARY_DIR}/embedded_plugins.c"
    ${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"
    # WASI doesn't have the current directory but wasi-libc emulates it.
    # A user of libgroonga.wasm needs this to use a relative path.
    "-Wl,--export=chdir"
    "-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)
  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(GRN_EMBED)
  # A plugin uses this to name its entry points uniquely.
  target_compile_definitions(libgroonga PUBLIC GRN_EMBEDDED)
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(WASI)
  # See GRN_PLUGIN_LIBGROONGA in the top level CMakeLists.txt.
  add_library(grn_plugin_dependencies INTERFACE)
  target_include_directories(
    grn_plugin_dependencies
    INTERFACE $<TARGET_PROPERTY:libgroonga,INTERFACE_INCLUDE_DIRECTORIES>)
  target_compile_definitions(
    grn_plugin_dependencies
    INTERFACE $<TARGET_PROPERTY:libgroonga,INTERFACE_COMPILE_DEFINITIONS>)
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()

target_link_libraries(libgroonga PRIVATE grn_dependencies
                                         ${GRN_EMBEDDED_PLUGIN_TARGETS})
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}")
elseif(NOT GRN_EMBED)
  target_include_directories(
    libgroonga
    INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/groonga>")
  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()
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()
