
#  .d8888b.           888
# d88P  Y88b          888
# Y88b.               888
#  "Y888b.    .d88b.  888888 888  888 88888b.
#     "Y88b. d8P  Y8b 888    888  888 888 "88b
#       "888 88888888 888    888  888 888  888
# Y88b  d88P Y8b.     Y88b.  Y88b 888 888 d88P
#  "Y8888P"   "Y8888   "Y888  "Y88888 88888P"
#                                     888
#                                     888
#                                     888

cmake_minimum_required (VERSION 3.15...4.0)

project (libbson
   LANGUAGES C
   # Inherit the version from mongo-c-driver
   VERSION "${PROJECT_VERSION}"
   DESCRIPTION "The libbson BSON serialization library"
)

# These values are inherited from the mongo-c-driver parent. These are named as to
# match the CMake variables generated by project().
set(libbson_VERSION_PRERELEASE ${mongo-c-driver_VERSION_PRERELEASE})
set(libbson_VERSION_FULL ${mongo-c-driver_VERSION_FULL})

# Don't export symbols implicitly
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

#  .d8888b.           888    888    d8b
# d88P  Y88b          888    888    Y8P
# Y88b.               888    888
#  "Y888b.    .d88b.  888888 888888 888 88888b.   .d88b.  .d8888b
#     "Y88b. d8P  Y8b 888    888    888 888 "88b d88P"88b 88K
#       "888 88888888 888    888    888 888  888 888  888 "Y8888b.
# Y88b  d88P Y8b.     Y88b.  Y88b.  888 888  888 Y88b 888      X88
#  "Y8888P"   "Y8888   "Y888  "Y888 888 888  888  "Y88888  88888P'
#                                                     888
#                                                Y8b d88P
#                                                 "Y88P"

# libbson-specific configuration settings. Note that we inherit all settings
# from the parent!

mongo_setting(
   BSON_OUTPUT_BASENAME "Set the output basename for the libbson library"
   TYPE STRING
   DEFAULT VALUE "bson"
)
# Control over what components are installed:
mongo_bool_setting(
   ENABLE_STATIC_LIBBSON_INSTALL "Install static libbson"
   VISIBLE_IF ENABLE_STATIC)
mongo_bool_setting(
   ENABLE_SHARED_LIBBSON_INSTALL "Install shared libbson"
   VISIBLE_IF ENABLE_SHARED)


#  .d8888b.  888                        888
# d88P  Y88b 888                        888
# 888    888 888                        888
# 888        88888b.   .d88b.   .d8888b 888  888 .d8888b
# 888        888 "88b d8P  Y8b d88P"    888 .88P 88K
# 888    888 888  888 88888888 888      888888K  "Y8888b.
# Y88b  d88P 888  888 Y8b.     Y88b.    888 "88b      X88
#  "Y8888P"  888  888  "Y8888   "Y8888P 888  888  88888P'

# Configure-time platform checks. These start as regular CMake booleans, but are
# converted to 0/1 values (with mongo_bool01) so that they can be inserted into
# bson/config.h as preprocessor values. We cannot use #cmakedefine01, as we need
# to keep compatibility with an external Autotools-generated library configuration

include (CheckFunctionExists)
include (CheckIncludeFile)
include (CheckStructHasMember)
include (CheckSymbolExists)
include (TestBigEndian)
include (InstallRequiredSystemLibraries)
include (CheckIncludeFiles)

# See https://public.kitware.com/Bug/view.php?id=15659
check_symbol_exists (snprintf stdio.h BSON_HAVE_SNPRINTF)
mongo_bool01 (BSON_HAVE_SNPRINTF BSON_HAVE_SNPRINTF)
check_struct_has_member ("struct timespec" tv_sec time.h BSON_HAVE_TIMESPEC)
mongo_bool01 (BSON_HAVE_TIMESPEC BSON_HAVE_TIMESPEC)
check_symbol_exists (gmtime_r time.h BSON_HAVE_GMTIME_R)
mongo_bool01 (BSON_HAVE_GMTIME_R BSON_HAVE_GMTIME_R)
check_function_exists (rand_r BSON_HAVE_RAND_R)
mongo_bool01 (BSON_HAVE_RAND_R BSON_HAVE_RAND_R)
check_include_file (strings.h BSON_HAVE_STRINGS_H)
mongo_bool01 (BSON_HAVE_STRINGS_H BSON_HAVE_STRINGS_H)
check_symbol_exists (strlcpy string.h BSON_HAVE_STRLCPY)
mongo_bool01 (BSON_HAVE_STRLCPY BSON_HAVE_STRLCPY)
check_include_file (stdbool.h BSON_HAVE_STDBOOL_H)
mongo_bool01 (BSON_HAVE_STDBOOL_H BSON_HAVE_STDBOOL_H)
check_symbol_exists (clock_gettime time.h BSON_HAVE_CLOCK_GETTIME)
mongo_bool01 (BSON_HAVE_CLOCK_GETTIME BSON_HAVE_CLOCK_GETTIME)
check_symbol_exists (strnlen string.h BSON_HAVE_STRNLEN)
mongo_bool01 (BSON_HAVE_STRNLEN BSON_HAVE_STRNLEN)
check_symbol_exists (aligned_alloc stdlib.h BSON_HAVE_ALIGNED_ALLOC)
mongo_bool01 (BSON_HAVE_ALIGNED_ALLOC BSON_HAVE_ALIGNED_ALLOC)
test_big_endian (BSON_BIG_ENDIAN)

if (WIN32)
   set (BSON_OS 2)
else ()
   set (BSON_OS 1)
endif ()

if (BSON_BIG_ENDIAN)
   set (BSON_BYTE_ORDER 4321)
else ()
   set (BSON_BYTE_ORDER 1234)
endif ()

configure_file (
   "${PROJECT_SOURCE_DIR}/src/bson/config.h.in"
   "${PROJECT_BINARY_DIR}/src/bson/config.h"
)

configure_file (
   "${PROJECT_SOURCE_DIR}/src/bson/version.h.in"
   "${PROJECT_BINARY_DIR}/src/bson/version.h"
)

# 8888888b.            .d888 d8b          d8b 888    d8b
# 888  "Y88b          d88P"  Y8P          Y8P 888    Y8P
# 888    888          888                     888
# 888    888  .d88b.  888888 888 88888b.  888 888888 888  .d88b.  88888b.  .d8888b
# 888    888 d8P  Y8b 888    888 888 "88b 888 888    888 d88""88b 888 "88b 88K
# 888    888 88888888 888    888 888  888 888 888    888 888  888 888  888 "Y8888b.
# 888  .d88P Y8b.     888    888 888  888 888 Y88b.  888 Y88..88P 888  888      X88
# 8888888P"   "Y8888  888    888 888  888 888  "Y888 888  "Y88P"  888  888  88888P'

# Base INTERFACE library propagates in-build-tree requirements for using libbson
add_library(_libbson_build_interface INTERFACE)
# Header directories required for libbson in-source
target_include_directories(_libbson_build_interface INTERFACE
   # The default src/
   src/
   # The generated src/
   ${PROJECT_BINARY_DIR}/src/
   # The source directories of the common lib:
   ${mongo-c-driver_SOURCE_DIR}/src/common/src/
   # Generated:
   ${mongo-c-driver_BINARY_DIR}/src/common/src/
   )
target_link_libraries(_libbson_build_interface INTERFACE mongo::detail::c_platform)

# Collect all source files
file(GLOB_RECURSE all_sources CONFIGURE_DEPENDS
   src/*.c src/*.h
   # Include common library source files within libbson itself (names
   # will be mangled with MCOMMON_NAME_PREFIX)
   "${mongo-c-driver_SOURCE_DIR}/src/common/src/*.c"
   )

# List of the primary BSON library targets that we are building
set(bson_libs)

if(ENABLE_STATIC)
   add_library(bson_static STATIC ${all_sources})
   add_library(bson::static ALIAS bson_static)
   set_property(TARGET bson_static PROPERTY EXPORT_NAME bson::static)
   list(APPEND bson_libs bson_static)
   # Define `BSON_STATIC` to suppress __declspec(dllexport/dllimport) when compiling
   # and consuming the static library
   target_compile_definitions(bson_static PUBLIC BSON_STATIC)

   if(ENABLE_PIC)
      # User wants static libs to use PIC code.
      set_property(TARGET bson_static PROPERTY POSITION_INDEPENDENT_CODE TRUE)
   endif()

   if(MONGO_CAN_VERIFY_HEADERS AND ENABLE_TESTS)
      mongo_verify_headers(
         bson
         USE_LIBRARIES bson::static
         HEADERS
            # Grab all headers
            "src/*.h"
         EXCLUDE_REGEX
            # No -private headers
            "-private\\.h"
            # Ignore all headers that start with "bson-", as they have not
            # been sanitized for direct inclusion.
            "bson/bson-.*\\.h"
            # Don't check jsonsl.h either
            "jsonsl\\.h"
         )
   endif()
endif()

if(ENABLE_SHARED)
   add_library(bson_shared SHARED ${all_sources})
   add_library(bson::shared ALIAS bson_shared)
   set_property(TARGET bson_shared PROPERTY EXPORT_NAME bson::shared)
   if(WIN32)
      # Add resource-definition script for Windows shared library (.dll).
      configure_file(libbson.rc.in libbson.rc)
      target_sources(bson_shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libbson.rc)
   endif()
   list(APPEND bson_libs bson_shared)
endif()

if(NOT bson_libs)
   message(FATAL_ERROR "Neither bson_shared nor bson_static is going to be built. Did you mean to enable at least one of them?")
endif()

# Set target properties for the libraries.
mongo_target_requirements(
   ${bson_libs}
   LINK_LIBRARIES
      PUBLIC
         # Build-local requirements:
         $<BUILD_INTERFACE:_libbson_build_interface>
         # Include in the install interface explicitly:
         mongo::detail::c_platform
   COMPILE_DEFINITIONS
      PRIVATE
         # Tell headers that they are part of compilation:
         BSON_COMPILATION
         # Enable NaN parsing in jsonsl
         JSONSL_PARSE_NAN
         # Set the name mangling scheme for the common libraries
         MCOMMON_NAME_PREFIX=_bson_mcommon
   COMPILE_OPTIONS
      PRIVATE
         # Enable unconditional warnings-as-errors for our source code.
         ${mongoc-warning-options}

         # Macro constant INFINITY triggers constant arithmetic overflow warnings in
         # VS 2013, but VS 2013 doesn't support inline warning suppression.
         # Remove once support for VS 2013 is dropped.
         $<$<AND:$<C_COMPILER_ID:MSVC>,$<VERSION_LESS:${MSVC_VERSION},1900>>:/wd4756>
)

set_target_properties(${bson_libs} PROPERTIES
   VERSION "${PROJECT_VERSION}"
   SOVERSION "${PROJECT_VERSION_MAJOR}"
   OUTPUT_NAME "${BSON_OUTPUT_BASENAME}${PROJECT_VERSION_MAJOR}"
)

# 8888888888                                          888
# 888                                                 888
# 888                                                 888
# 8888888    888  888  8888b.  88888b.d88b.  88888b.  888  .d88b.  .d8888b
# 888        `Y8bd8P'     "88b 888 "888 "88b 888 "88b 888 d8P  Y8b 88K
# 888          X88K   .d888888 888  888  888 888  888 888 88888888 "Y8888b.
# 888        .d8""8b. 888  888 888  888  888 888 d88P 888 Y8b.          X88
# 8888888888 888  888 "Y888888 888  888  888 88888P"  888  "Y8888   88888P'
#                                            888
#                                            888
#                                            888

if (ENABLE_EXAMPLES)
   function (add_example bin src)
      add_executable (${bin} EXCLUDE_FROM_ALL ${src})
      add_dependencies (mongo_c_driver_examples ${bin})

      # Enable unconditional warnings-as-errors for our source code.
      target_compile_options (${bin} PRIVATE ${mongoc-warning-options})

      # Link against the shared lib like normal apps
      if(TARGET bson_shared)
         target_link_libraries (${bin} bson_shared)
      elseif(TARGET bson_static)
         target_link_libraries (${bin} bson_static)
      else()
         return()
      endif()
   endfunction ()

   add_example (bcon-col-view examples/bcon-col-view.c)
   add_example (bcon-speed examples/bcon-speed.c)
   add_example (bson-metrics examples/bson-metrics.c)
   if (NOT WIN32)
      target_link_libraries (bson-metrics m)
      add_example (bson-streaming-reader examples/bson-streaming-reader.c)
   endif ()
   add_example (bson-to-json examples/bson-to-json.c)
   add_example (bson-validate examples/bson-validate.c)
   add_example (json-to-bson examples/json-to-bson.c)
   add_example (bson-check-depth examples/bson-check-depth.c)
   add_example (creating examples/creating.c)
   add_example (extended-json examples/extended-json.c)
endif () # ENABLE_EXAMPLES


# 8888888                   888             888 888
#   888                     888             888 888
#   888                     888             888 888
#   888   88888b.  .d8888b  888888  8888b.  888 888
#   888   888 "88b 88K      888        "88b 888 888
#   888   888  888 "Y8888b. 888    .d888888 888 888
#   888   888  888      X88 Y88b.  888  888 888 888
# 8888888 888  888  88888P'  "Y888 "Y888888 888 888

# Infix directory for all libbson headers.
if(NOT DEFINED BSON_INSTALL_INCLUDEDIR)
   set(BSON_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/bson-${PROJECT_VERSION}")
endif()
# Infix directory for all libbson CMake package files
if(NOT DEFINED BSON_INSTALL_CMAKEDIR)
   set(BSON_INSTALL_CMAKEDIR "${MONGO_C_DRIVER_INSTALL_CMAKEDIR}/bson-${PROJECT_VERSION}")
endif()

# pkg-config properties:
set_directory_properties(PROPERTIES
   pkg_config_INCLUDE_DIRECTORIES "${BSON_INSTALL_INCLUDEDIR}"
   pkg_config_NAME "bson"
)

function(install_export_target target)
   # Install the target:
   install(
      TARGETS "${target}"
      # Important: We generate a unique export set for this target alone.
      EXPORT "${target}-targets"
      LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
      INCLUDES DESTINATION "${BSON_INSTALL_INCLUDEDIR}"
   )
   # Install the unique export set into a file that is qualified by the name of
   # the target itself. The main config-file package will search for the
   # possibly-installed exported targets for the known targets.
   install(
      EXPORT "${target}-targets"
      FILE "${target}-targets.cmake"
      DESTINATION "${BSON_INSTALL_CMAKEDIR}"
   )
endfunction()

if(ENABLE_STATIC_LIBBSON_INSTALL AND TARGET bson_static)
   install_export_target(bson_static)
   mongo_generate_pkg_config(bson_static FILENAME bson${PROJECT_VERSION_MAJOR}-static.pc INSTALL)
endif()
if(ENABLE_SHARED_LIBBSON_INSTALL AND TARGET bson_shared)
   install_export_target(bson_shared)
   mongo_generate_pkg_config(bson_shared FILENAME bson${PROJECT_VERSION_MAJOR}.pc INSTALL)
endif()

# Install all headers by doing a recursive directory-install. In the future, this
# can be better handled by installing the headers as a file set via target_sources()
install(
   DIRECTORY
      # Trailing "/" requests directory contents, not the dir itself:
      src/
      # Also get the generated dir:
      ${PROJECT_BINARY_DIR}/src/
   DESTINATION "${BSON_INSTALL_INCLUDEDIR}"
   # Only grab the *public* headers.
   FILES_MATCHING
      PATTERN "*.h"
      PATTERN "*-private.h" EXCLUDE
      # Don't install jsonsl headers
      PATTERN "jsonsl" EXCLUDE
   )

# Install the platform libraries from MongoPlatform.cmake. Search: PLATFORM-EXPORT-TARGET-INSTALL
install(EXPORT mongo-platform-targets
   # The "00" prefix forces the file to sort to the top during globbing, because
   # we want it to be included first before the libbson targets
   FILE 00-mongo-platform-targets.cmake
   DESTINATION "${BSON_INSTALL_CMAKEDIR}"
)

# Install the CMake config-mode package
configure_file(
   ${mongo-c-driver_SOURCE_DIR}/build/cmake/packageConfigVersion.cmake.in
   bsonConfigVersion.cmake
   @ONLY
)
install(
   FILES etc/bsonConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/bsonConfigVersion.cmake"
   DESTINATION "${BSON_INSTALL_CMAKEDIR}"
)

include (CPack)

if (MONGO_FUZZ)
   add_subdirectory(fuzz)
endif ()

# 8888888b.
# 888  "Y88b
# 888    888
# 888    888  .d88b.   .d8888b .d8888b
# 888    888 d88""88b d88P"    88K
# 888    888 888  888 888      "Y8888b.
# 888  .d88P Y88..88P Y88b.         X88
# 8888888P"   "Y88P"   "Y8888P  88888P'

if (ENABLE_MAN_PAGES OR ENABLE_HTML_DOCS)
   find_package (Sphinx REQUIRED)
   add_subdirectory (doc)
   add_custom_target (bson-doc
      ALL
      DEPENDS
      $<TARGET_NAME_IF_EXISTS:bson-man>
      $<TARGET_NAME_IF_EXISTS:bson-html>
   )
endif ()
