# Copyright (c) 2007, 2024, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is designed to work with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms, as
# designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have either included with
# the program or referenced in the documentation.
#
# This program 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, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

##########################################################################

PROJECT(MySQL_Connector_ODBC)

CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR)

if(COMMAND cmake_policy)
  cmake_policy(VERSION 3.0)
  cmake_policy(SET CMP0054 NEW)
  cmake_policy(SET CMP0003 NEW)
  cmake_policy(SET CMP0022 NEW)
# cmake_policy(SET CMP0026 OLD)
endif(COMMAND cmake_policy)


#------- compatibility with older cmake versions ------

#message("CMAKE_VERSION: ${CMAKE_VERSION}")

if(CMAKE_VERSION VERSION_LESS "3.0")

SET(BUILDTYPE_DOCSTRING
 "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or
 CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel")

#
# If cmake is invoked with -DCMAKE_BUILD_TYPE,
# respect user wishes and do not (re)define CMAKE_BUILD_TYPE. Otherwise,
# use Relwithdebinfo.
#

#IF(NOT DEFINED CMAKE_BUILD_TYPE)
#  SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING
#  ${BUILDTYPE_DOCSTRING} FORCE)
#ENDIF()

macro(add_compile_options)
  add_definitions(${ARGV})
endmacro()

else()

  set_property(GLOBAL PROPERTY USE_FOLDERS ON)

endif()

#-----------------------------------------------------

include(TestBigEndian)
test_big_endian(BIG_ENDIAN)
message(STATUS "BIG_ENDIAN: ${BIG_ENDIAN}")
if (BIG_ENDIAN)
  add_definitions(-DIS_BIG_ENDIAN)
endif()

#-----------------------------------------------------

FIND_PACKAGE(Threads)

INCLUDE(version.cmake)

IF(NOT WIN32)
  INCLUDE(CheckFunctionExists)
  CHECK_FUNCTION_EXISTS(dlopen DLOPEN_IN_LIBC)
    SET(DL_INCLUDES)
    SET(DL_LFLAGS)
    SET(DL_LIBS)
    IF(NOT DLOPEN_IN_LIBC)
      FIND_LIBRARY(DL_LIBS dl)
    ENDIF(NOT DLOPEN_IN_LIBC)
    IF(DLOPEN_IN_LIBC OR DL_LIBS)
      ADD_DEFINITIONS(-DHAVE_LIBDL)
    ENDIF()
ENDIF(NOT WIN32)

# RPATH handling
SET(CMAKE_SKIP_BUILD_RPATH  FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH "")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)

#Default is to build both drivers
IF(UNICODE OR NOT ANSI)
  MESSAGE(STATUS "Configuring to build Unicode driver")
  SET(IS_UNICODE_DRIVER 1)
  SET(CONNECTOR_DRIVER_TYPE "Unicode")
  SET(CONNECTOR_DRIVER_TYPE_LIST "Unicode")
  SET(CONNECTOR_DRIVER_TYPES_SHORT "w")
  SET(PACKAGE_DRIVER_TYPES_SUFFIX "")
ENDIF(UNICODE OR NOT ANSI)

IF(ANSI OR NOT UNICODE)
  MESSAGE(STATUS "Configuring to build ANSI driver")
  SET(IS_UNICODE_DRIVER ${IS_UNICODE_DRIVER} 0)
  # We need space separated string
  IF(UNICODE OR NOT ANSI)
    SET(CONNECTOR_DRIVER_TYPE "${CONNECTOR_DRIVER_TYPE} ANSI")
  ELSE(UNICODE OR NOT ANSI)
    SET(CONNECTOR_DRIVER_TYPE "ANSI")
  ENDIF(UNICODE OR NOT ANSI)

  LIST(APPEND CONNECTOR_DRIVER_TYPE_LIST "ANSI")

  SET(CONNECTOR_DRIVER_TYPES_SHORT ${CONNECTOR_DRIVER_TYPES_SHORT} "a")
  SET(PACKAGE_DRIVER_TYPES_SUFFIX ${PACKAGE_DRIVER_TYPE_SUFFIX} "-ansi")
ENDIF(ANSI OR NOT UNICODE)

SET(EDIT_WARNING_MESSAGE "Please do not edit this file - it is generated by cmake. Edit its source file instead.")

LIST(LENGTH IS_UNICODE_DRIVER DRIVERS_COUNT)

IF(WIN32)
  SET(DEFAULT_MYSQL_HOME "C:/Program Files/MySQL/MySQL Server ${MYSQL_BASE_VERSION}" )
  SET(SHAREDIR share)
ELSE()
  SET(DEFAULT_MYSQL_HOME ${CMAKE_INSTALL_PREFIX})
  SET(SHAREDIR ${DEFAULT_MYSQL_HOME}/${INSTALL_MYSQLSHAREDIR})
ENDIF()

SET(DEFAULT_BASEDIR "${DEFAULT_MYSQL_HOME}")
IF(INSTALL_MYSQLDATADIR MATCHES "^/.*")
  SET(MYSQL_DATADIR ${INSTALL_MYSQLDATADIR} CACHE PATH "default MySQL data directory")
ELSE()
  SET(MYSQL_DATADIR "${DEFAULT_MYSQL_HOME}/${INSTALL_MYSQLDATADIR}" CACHE PATH "default MySQL data directory")
ENDIF()
IF(INSTALL_MYSQLKEYRINGDIR MATCHES "^/.*")
  SET(MYSQL_KEYRINGDIR ${INSTALL_MYSQLKEYRINGDIR} CACHE PATH "default MySQL keyring directory")
ELSE()
  SET(MYSQL_KEYRINGDIR "${DEFAULT_MYSQL_HOME}/${INSTALL_MYSQLKEYRINGDIR}" CACHE PATH "default MySQL keyring directory")
ENDIF()
SET(DEFAULT_CHARSET_HOME "${DEFAULT_MYSQL_HOME}")
SET(PLUGINDIR "${DEFAULT_MYSQL_HOME}/${INSTALL_PLUGINDIR}")
IF(SYSCONFDIR)
  SET(DEFAULT_SYSCONFDIR "${SYSCONFDIR}")
ENDIF()

#
# Macro to enable C++14 for all targets in the current directory and below.
#

macro(enable_cxx14)

if((CMAKE_VERSION VERSION_LESS 3.1) OR (CMAKE_CXX_COMPILER_ID MATCHES "SunPro"))

  # Note: cmake does not know how to enable C++14 for SunPro compiler
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")

else()

  # use C++14
  set(CMAKE_CXX_STANDARD 14)
  # error out if not supported
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  # do not use compiler specific extensions
  set(CMAKE_CXX_EXTENSIONS OFF)

endif()

endmacro()

macro(enable_cxx17)

if((CMAKE_VERSION VERSION_LESS 3.1) OR (CMAKE_CXX_COMPILER_ID MATCHES "SunPro"))

  # Note: cmake does not know how to enable C++17 for SunPro compiler
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")

else()

  # use C++14
  set(CMAKE_CXX_STANDARD 17)
  # error out if not supported
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  # do not use compiler specific extensions
  set(CMAKE_CXX_EXTENSIONS OFF)

endif()

endmacro()

# With new mysys headers, windows needs c++17, and other platforms c++14.
if(WIN32)
  enable_cxx17()
else()
  enable_cxx14()
endif()

if(APPLE)
  set(CMAKE_MACOSX_RPATH ON)
endif()

INCLUDE(CheckTypeSize)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/character_sets.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/libutils.cmake)

# We need some extra FAIL_REGEX patterns
# Note that CHECK_C_SOURCE_COMPILES is a misnomer, it will also link.
MACRO (MY_CHECK_C_COMPILER_FLAG FLAG RESULT)
  SET(SAVE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
  SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
  CHECK_C_SOURCE_COMPILES("int main(void) { return 0; }" ${RESULT}
    FAIL_REGEX "argument unused during compilation"
    FAIL_REGEX "unsupported .*option"
    FAIL_REGEX "unknown .*option"
    FAIL_REGEX "unrecognized .*option"
    FAIL_REGEX "ignoring unknown option"
    FAIL_REGEX "[Ww]arning: [Oo]ption"
    FAIL_REGEX "error: visibility"
    FAIL_REGEX "warning: visibility"
    )
  SET(CMAKE_REQUIRED_FLAGS "${SAVE_CMAKE_REQUIRED_FLAGS}")
ENDMACRO()

MACRO (MY_CHECK_CXX_COMPILER_FLAG FLAG RESULT)
  SET(SAVE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
  SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
  CHECK_CXX_SOURCE_COMPILES("int main(void) { return 0; }" ${RESULT}
    FAIL_REGEX "argument unused during compilation"
    FAIL_REGEX "unsupported .*option"
    FAIL_REGEX "unknown .*option"
    FAIL_REGEX "unrecognized .*option"
    FAIL_REGEX "ignoring unknown option"
    FAIL_REGEX "[Ww]arning: [Oo]ption"
    FAIL_REGEX "error: visibility"
    FAIL_REGEX "warning: visibility"
    )
  SET(CMAKE_REQUIRED_FLAGS "${SAVE_CMAKE_REQUIRED_FLAGS}")
ENDMACRO()

INCLUDE(configure.cmake)

#-------------- Coverage --------------------
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/gcov.cmake)
#-------------- find  mysql --------------------
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindMySQL.cmake)
#-----------------------------------------------------


IF(MYSQL_VERSION_ID GREATER 80002)
  ADD_DEFINITIONS(-DMYSQL8)
  SET(MYSQL8 1)
ENDIF(MYSQL_VERSION_ID GREATER 80002)

IF(MYSQLCLIENT_STATIC_LINKING)
  ADD_DEFINITIONS(-DMYSQLCLIENT_STATIC_LINKING)
ENDIF(MYSQLCLIENT_STATIC_LINKING)

IF(MYSQL8)
  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config8.h.cmake ${CMAKE_SOURCE_DIR}/include/mysql-8.0/my_config.h)
  INCLUDE_DIRECTORIES(include/mysql-8.0)
ELSE()
  # Run platform tests
  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_SOURCE_DIR}/include/sys/my_config.h)
ENDIF(MYSQL8)


CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/VersionInfo.h.cmake ${CMAKE_SOURCE_DIR}/VersionInfo.h @ONLY)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/macosx/postflight.in ${CMAKE_SOURCE_DIR}/scripts/macosx/postflight @ONLY)
IF(WIN32)
  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/Install.bat.cmake ${CMAKE_SOURCE_DIR}/Install.bat @ONLY)
  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/Uninstall.bat.cmake ${CMAKE_SOURCE_DIR}/Uninstall.bat @ONLY)
ENDIF(WIN32)

#SET(CMAKE_VERBOSE_MAKEFILE ON)

IF(NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
        SET(DISABLE_GUI 1)
ENDIF(NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")

# Disable mysql client debug and PSI
ADD_DEFINITIONS(-DNDEBUG -DDISABLE_ALL_PSI)

#-------------- unixodbc/iodbc/win -------------------
IF(WIN32)
        SET(ODBCLIB odbc32)
        IF (ODBCCP32_LIB)
          SET(ODBCINSTLIB ${ODBCCP32_LIB})
        ELSE (ODBCCP32_LIB)
          SET(ODBCINSTLIB odbccp32)
        ENDIF (ODBCCP32_LIB)
        MESSAGE(STATUS "ODBC Installation Library: ${ODBCINSTLIB}")
ELSE(WIN32)
        IF(WITH_UNIXODBC)
                SET(ODBCLIB odbc)
                SET(ODBCINSTLIB odbcinst)
        ELSE(WITH_UNIXODBC)

            IF(ODBCINST_LIB_DIR)
                SET(ODBCLIB iODBC)
                SET(ODBCINSTLIB iODBCinst)
            ELSE(ODBCINST_LIB_DIR)
                SET(ODBCLIB iodbc)
                SET(ODBCINSTLIB iodbcinst)
            ENDIF(ODBCINST_LIB_DIR)

        ENDIF(WITH_UNIXODBC)

        # FindODBC uses ODBCLIB and ODBCINSTLIB in some cases
        INCLUDE(cmake/FindODBC.cmake)

        IF(WITH_UNIXODBC)
                TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/havelpcwstr.c
                        CMAKE_FLAGS "-DINCLUDE_DIRECTORIES='${ODBC_INCLUDES} ${ODBC_INCLUDE_DIR}'")
                MESSAGE(STATUS "Checking if LPCWSTR type is present - ${COMPILE_RESULT}")

                IF(COMPILE_RESULT)
                        ADD_DEFINITIONS(-DHAVE_LPCWSTR)
                        ADD_DEFINITIONS(-DUSE_UNIXODBC)
                ENDIF(COMPILE_RESULT)

  ELSE(WITH_UNIXODBC)
     # ADD_DEFINITIONS(-DHAVE_SQLGETPRIVATEPROFILESTRINGW)
     #---- if it is not UnixODBC we assume iODBC -------
     ADD_DEFINITIONS(-DUSE_IODBC)
  ENDIF(WITH_UNIXODBC)

ENDIF(WIN32)

#-----------------------------------------------------

#-------- configuring paths to odbc heders for compatibility checks ---------
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlcolattrib1.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib1.c @ONLY)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlcolattrib2.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib2.c @ONLY)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlparamopt1.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlparamopt1.c @ONLY)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlparamopt2.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlparamopt2.c @ONLY)
#-----------------------------------------------------

#------------------ check compatibility---------------
TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib1.c)
MESSAGE(STATUS "Checking if SQLColAttribute last arg is compatible with SQLLEN* - ${COMPILE_RESULT}")

IF(COMPILE_RESULT)
        ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLLEN_PTR)
ELSE(COMPILE_RESULT)
        TRY_COMPILE(COMPILE_RESULT1 ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib2.c)
        MESSAGE(STATUS "Checking if SQLColAttribute last arg is compatible with SQLPOINTER - ${COMPILE_RESULT1}")

        IF(COMPILE_RESULT1)
                ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLPOINTER)
        ELSE(COMPILE_RESULT1)
                # By default using SQLLEN parameter
                ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLLEN_PTR)
        ENDIF(COMPILE_RESULT1)
ENDIF(COMPILE_RESULT)

TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlparamopt1.c)
MESSAGE(STATUS "Checking if SQLParamOptions() 2nd and 3rd arg is compatible with SQLULEN - ${COMPILE_RESULT}")

IF(COMPILE_RESULT)
        ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLULEN_PTR)
ELSE(COMPILE_RESULT)
        TRY_COMPILE(COMPILE_RESULT1 ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlparamopt2.c)
        MESSAGE(STATUS "Checking if SQLParamOptions() 2nd and 3rd arg is compatible with SQLUINTEGER - ${COMPILE_RESULT1}")

        IF(COMPILE_RESULT1)
                ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLUINTEGER_PTR)
        ELSE(COMPILE_RESULT1)
                # SQLULEN is a default
    MESSAGE(STATUS "Apparently odbc headers could not be found. 2nd and 3rd parameters assumed to be (*)SQLULEN by default")
                ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLULEN_PTR)
        ENDIF(COMPILE_RESULT1)

ENDIF(COMPILE_RESULT)
#-----------------------------------------------------

  IF(NOT NO_THREADS)
    MESSAGE(STATUS "Enabling threads support")
    ADD_DEFINITIONS(-DTHREAD)
  ENDIF(NOT NO_THREADS)

#------------ build options for windows --------------

set(MAINTAINER_MODE OFF CACHE BOOL "Build sources in maintainer mode")
mark_as_advanced(MAINTAINER_MODE)

IF(WIN32)
        REMOVE_DEFINITIONS(-DUNICODE)
        ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
        ADD_DEFINITIONS(-DENGLISH -D_USERDLL)
        ADD_DEFINITIONS(-D_WIN32 -DWIN32 -D_WINDOWS -D__WIN__)

  #Since 5.5.13 libmysql introduces dependency on Secur32.lib
  FILE(STRINGS "${MYSQL_INCLUDE_DIR}\\mysql_version.h" mysql_version REGEX "^\#define[ \t]+MYSQL_VERSION_ID")

  STRING(REGEX REPLACE "^\#define[ \t]+MYSQL_VERSION_ID[ \t]+([0-9]+)" "\\1" MYSQL_CLIENT_VERSION "${mysql_version}")

  IF(MYSQL_CLIENT_VERSION GREATER 50512)
    MESSAGE(STATUS "MySQL client lib(version ${MYSQL_CLIENT_VERSION}) requires Secure32.lib - TRUE")
    SET(SECURE32_LIB Secur32)
  ENDIF(MYSQL_CLIENT_VERSION GREATER 50512)

        # edits for all config build flags
        FOREACH(TYPE C CXX)
                # makefiles use blank configuration
                FOREACH(CFG "_DEBUG" "_MINSIZEREL" "_RELEASE" "_RELWITHDEBINFO")

                        SET(NEW_FLAGS "${CMAKE_${TYPE}_FLAGS${CFG}}")

                        # fix up static libc flags
                        IF(STATIC_MSVCRT)
                          STRING(REPLACE "/MD" "/MT" NEW_FLAGS "${NEW_FLAGS}")
                          IF(CFG STREQUAL "_DEBUG")
                            SET(NEW_FLAGS "${NEW_FLAGS} /MTd")
                          ELSEIF()
                            SET(NEW_FLAGS "${NEW_FLAGS} /MT")
                          ENDIF()
                        ELSEIF()
                          STRING(REPLACE "/MT" "/MD" NEW_FLAGS "${NEW_FLAGS}")
                          IF(CFG STREQUAL "_DEBUG")
                            SET(NEW_FLAGS "${NEW_FLAGS} /MDd")
                          ELSEIF()
                            SET(NEW_FLAGS "${NEW_FLAGS} /MD")
                          ENDIF()
                        ENDIF()

                        # Add some additional help for debug builds
                        IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
                                STRING(REPLACE "/Zi" "/ZI" NEW_FLAGS "${NEW_FLAGS}")
                                SET(NEW_FLAGS "${NEW_FLAGS} /RTC1 ")
                        ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")

                        # *FORCE* to override whats already placed into the cache
                        SET(CMAKE_${TYPE}_FLAGS${CFG} "${NEW_FLAGS}" CACHE STRING
                                "CMAKE_${TYPE}_FLAGS${CFG} (overwritten for odbc)" FORCE)

                ENDFOREACH(CFG)
        ENDFOREACH(TYPE)

  if (FIX_WARNINGS)

    # TODO: Increase warning level and once all warnings are cleared, enable
    # higher warning level in MAINTAINER_MODE. We start with no warnings on
    # level /W1

    add_compile_options(/W1)

  else()

    add_compile_options(/W1 /wd4133 /wd4047 /wd4313 /wd4197)

  endif()


ELSE(WIN32)

  ADD_DEFINITIONS(-D_UNIX_)

  if(FIX_WARNINGS)

    # TODO: Fix the compile warnings generated without `-w`. Once done,
    # remove this option in MAINTAINER_MODE so that we see when new
    # warnings appear. Then proceed to `-w extra`.

    add_compile_options(-Werror)

    else()

    add_compile_options( -w )

  endif()

ENDIF(WIN32)

#-----------------------------------------------------

SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin")
SET(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/lib")

SET(LIB_SUBDIR "lib")
IF(RPM_BUILD AND CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|ppc64|ppc64p7|s390x|sparc64|aarch64)")
  SET(LIB_SUBDIR "lib64")
ENDIF()

MESSAGE(STATUS "Installation library subdir: ${LIB_SUBDIR}")

INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ssl_setup.cmake)
setup_ssl_libs()

INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR})

ADD_SUBDIRECTORY(util)
ADD_SUBDIRECTORY(driver)

IF(NOT DISABLE_GUI)
        ADD_SUBDIRECTORY(setupgui)
ENDIF(NOT DISABLE_GUI)

ADD_SUBDIRECTORY(dltest)
ADD_SUBDIRECTORY(installer)
ADD_SUBDIRECTORY(test)

# For dynamic linking use the built-in sys and strings
IF(NOT MYSQLCLIENT_STATIC_LINKING)
  ADD_SUBDIRECTORY(mysql_sys)
  ADD_SUBDIRECTORY(mysql_strings)
ENDIF()

##############################################################################
#
#  Packaging
#
##############################################################################

# Some stuff for building RPMs, that is not using CPack
# RPMs doesn't allow "-" in version, so we use like "x.x.0_alpha"
STRING(REPLACE "-" "_" CONNECTOR_NODASH_VERSION "${CONNECTOR_VERSION}")
IF(UNIX)
  EXECUTE_PROCESS(COMMAND "date" "+%Y" OUTPUT_VARIABLE CURRENT_YEAR OUTPUT_STRIP_TRAILING_WHITESPACE)
ENDIF()
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/myodbc3.spec.sh ${CMAKE_SOURCE_DIR}/myodbc3.spec @ONLY)

# The rest is mainly about CPack
SET(CPACK_PACKAGE_VERSION_MAJOR ${CONNECTOR_MAJOR})
SET(CPACK_PACKAGE_VERSION_MINOR ${CONNECTOR_MINOR})
SET(CPACK_PACKAGE_VERSION_PATCH ${CONNECTOR_PATCH})

IF(NOT EXTRA_NAME_SUFFIX)
  SET(EXTRA_NAME_SUFFIX "")
ENDIF(NOT EXTRA_NAME_SUFFIX)
IF(NOT EXTRA_NAME_SUFFIX2)
  SET(EXTRA_NAME_SUFFIX2 "")
ENDIF(NOT EXTRA_NAME_SUFFIX2)

SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Connector/ODBC ${CONNECTOR_BASE_VERSION}, a library for connecting to MySQL servers.")
SET(CPACK_PACKAGE_NAME                "mysql-connector-odbc${EXTRA_NAME_SUFFIX}")

SET(CPACK_RESOURCE_FILE_LICENSE     "${CMAKE_SOURCE_DIR}/LICENSE.txt")
if(EXISTS "${CMAKE_SOURCE_DIR}/README.txt")
  SET(CPACK_PACKAGE_DESCRIPTION_FILE    "${CMAKE_SOURCE_DIR}/README.txt")
else()
  SET(CPACK_PACKAGE_DESCRIPTION_FILE    "${CMAKE_SOURCE_DIR}/README.md")
endif()

SET(CPACK_SOURCE_PACKAGE_FILE_NAME    "${CPACK_PACKAGE_NAME}-${CONNECTOR_VERSION}-src")
SET(CPACK_PACKAGE_INSTALL_DIRECTORY   "${CPACK_PACKAGE_NAME}-${CONNECTOR_VERSION}${EXTRA_NAME_SUFFIX2}${PACKAGE_DRIVER_TYPE_SUFFIX}-${CONNECTOR_PLATFORM}")
IF(WIN32)
  SET(CPACK_GENERATOR                 "ZIP")
  SET(CPACK_PACKAGE_FILE_NAME         "${CPACK_PACKAGE_NAME}-noinstall-${CONNECTOR_VERSION}${EXTRA_NAME_SUFFIX2}${PACKAGE_DRIVER_TYPE_SUFFIX}-${CONNECTOR_PLATFORM}")
ELSE(WIN32)
  SET(CPACK_GENERATOR                 "TGZ")
  SET(CPACK_PACKAGE_FILE_NAME         "${CPACK_PACKAGE_INSTALL_DIRECTORY}")
ENDIF(WIN32)

SET(CPACK_SOURCE_IGNORE_FILES
\\\\.bzr/
\\\\.bzr-mysql
\\\\.bzrignore
CMakeCache\\\\.txt
CPackSourceConfig\\\\.cmake
CPackConfig\\\\.cmake
VersionInfo\\\\.h$
postflight$
/cmake_install\\\\.cmake
/CTestTestfile\\\\.cmake
/CMakeFiles/
/_CPack_Packages/
Makefile$
cmake/sql.*\\\\.c$
)

#------------ Installation ---------------------------
INCLUDE(cmake/info_bin.cmake)

INSTALL(FILES LICENSE.txt   DESTINATION .)

if(EXISTS "${CMAKE_SOURCE_DIR}/README.txt")
  INSTALL(FILES README.txt    DESTINATION .)
else()
  INSTALL(FILES README.md    DESTINATION .)
  INSTALL(FILES CONTRIBUTING.md    DESTINATION .)
endif()

IF(WIN32)

# For conveninence copy (un)install.bat scripts to the build
# directory to ease testing.

  CONFIGURE_FILE(Install.bat ${CMAKE_BINARY_DIR}/Install.bat COPYONLY)
  CONFIGURE_FILE(Uninstall.bat ${CMAKE_BINARY_DIR}/Uninstall.bat COPYONLY)

# TODO: line-ending conversions unix->dos

  INSTALL(FILES ChangeLog     DESTINATION . RENAME ChangeLog.txt)
  INSTALL(FILES Install.bat   DESTINATION .)
  INSTALL(FILES Uninstall.bat DESTINATION .)

  # Install all .pdb files to enable debugging. Note that what build
  # type and what sub directory the binaries ends up in, like
  # "Release" and "Debug", is not determined until we run "devenv" or
  # similar. So when running "cmake" we don't know the location. We
  # can't test for the location here, a IF(EXISTS ...) is run at
  # "cmake" invocation time, not when we are to install. So we do a
  # bit of a hack here until finding a better solution.

  # FIXME: Have an option to install PDBs also for Release builds?
  #
  # FIXME: Better control over PDBs that we install/ship -- for example
  # install only PDBs for specific targets using install(FILE ...) and
  # generator expression $<TARGET_PDB_FILE:tgt>

  INSTALL(DIRECTORY
    ${PROJECT_BINARY_DIR}/bin/RelWithDebInfo/
    DESTINATION bin
    CONFIGURATIONS RelWithDebInfo
    FILES_MATCHING PATTERN myodbc-installer.pdb
  )

  INSTALL(DIRECTORY
    ${PROJECT_BINARY_DIR}/bin/Debug/
    DESTINATION bin/Debug
    CONFIGURATIONS Debug
    FILES_MATCHING PATTERN myodbc-installer.pdb
  )

  INSTALL(DIRECTORY
    ${PROJECT_BINARY_DIR}/lib/RelWithDebInfo/
    DESTINATION lib
    CONFIGURATIONS RelWithDebInfo
    FILES_MATCHING PATTERN *.pdb
  )

  INSTALL(DIRECTORY
    ${PROJECT_BINARY_DIR}/lib/Debug/
    DESTINATION lib/Debug
    CONFIGURATIONS Debug
    FILES_MATCHING PATTERN *.pdb
  )

ELSE(WIN32)

  INSTALL(FILES ChangeLog    DESTINATION .)

ENDIF(WIN32)

#
# Bundle 3rd party dependencies if needed
# =======================================

# If build is configured with BUNDEL_DEPENDENCIES enabled then client-side
# plugins, their dependencies and other dependencies of the client library that
# are found at the client library installation location are copied to the
# location where ODBC driver is installed.
#
# The lists of known plugins and 3rd party libraries are specified below. Only
# the specified plugins and libraries will be bundled. Also plugins and
# libraries that can be found with the server but which should be ignored are
# specified below. When building in MAINTAINER_MODE cmake will report error if
# it finds a plugin or 3rd party librariy which is not listed here. When that
# happens the lists should be updated.
#
# TODO: Move these checks to cmake/FindMySQL.cmake ?
#

set(AUTH_PLUGINS
  ldap_sasl
  oci
)

if(NOT CMAKE_SYSTEM_NAME MATCHES "SunOS")
  list(APPEND AUTH_PLUGINS fido)
  # Note: Kerberos authentication plugin is not supported on macOS
  if(NOT APPLE)
    list(APPEND AUTH_PLUGINS kerberos)
  endif()
endif()

# Plugin dependencies.
#
# Warning: If one library name is a prefix of the other, the longer name
# should be listed first, otherwise the logic detecting missing dependencies
# will break... For example: `krb5support` must go before `krb5`

set(AUTH_DEPS_fido fido2)

if(WIN32)
  set(AUTH_DEPS_kerberos
    comerr gssapi k5sprt krbcc xpprof krb5
  )
else()
  set(AUTH_DEPS_kerberos gssapi_krb5 k5crypto krb5support krb5 com_err)
endif()

# Note: On Solaris and macOS all dependencies of ldap_sasl plugin are assumed
# to be part of the OS.

if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES "SunOS")
  set(AUTH_DEPS_ldap_sasl libsasl ${AUTH_DEPS_kerberos})
endif()
if(WIN32)
  list(APPEND AUTH_DEPS_ldap_sasl saslSCRAM)
endif()

# additional client-side plugins (if any)

set(PLUGINS)

# additional bundled dependencies of the client library

set(BUNDLED_LIBS
  libssl libcrypto ssleay libeay
)

# List plugins and other libraries that can be found bundled with the server
# but which are not relevant on client-side and can be safely ignored.

set(IGNORED_PLUGINS qa_auth_client telemetry_client)

set(IGNORED_LIBS
  abseil_dll  # Added in 8.0.37
  libmysql lber libprotobuf
  ldap  # note: this is needed only by server-side plugin
  libcurl libmecab zlib
  jemalloc
  # New DLLs added in .32
  connection_pool
  http_auth_backend http_auth_realm http_server
  io keepalive metadata_cache
  rest_api rest_connection_pool rest_metadata_cache rest_router rest_routing
  harness-library  mysqlharness_stdx mysqlharness_tls
  router_openssl router_protobuf routing
  mysqlrouter_mysqlxmessages
  mysqlrouter_connection_pool
  mysqlrouter_http
  mysqlrouter_http_auth_backend
  mysqlrouter_http_auth_realm
  mysqlrouter_io_component
  mysqlrouter_lib
  mysqlrouter_metadata_cache
  mysqlrouter_routing
  # New DLLs added in .33
  destination_status
  mysqlrouter_destination_status
)


# -------------

function(bundle_lib lib)

  get_filename_component(lib_name ${lib} NAME)

  if(bundled_${lib_name})
    return()
  endif()
  set(bundled_${lib_name} true PARENT_SCOPE)

  message("Bundling library: ${lib}")

  if(WIN32)
    install(FILES ${lib}
      CONFIGURATIONS Release RelWithDebInfo
      DESTINATION "${LIB_SUBDIR}"
      COMPONENT OdbcDll
    )
  elseif(APPLE)
    install(FILES ${lib}
      DESTINATION "${LIB_SUBDIR}"
      COMPONENT OdbcDll
    )
  else()
    install(FILES ${lib}
      DESTINATION "${LIB_SUBDIR}/private"
      COMPONENT OdbcDll
    )
  endif()

endfunction(bundle_lib)


# Bundle libraries listed in a list variable ${to_bundle}.
# Libraries that were found and bundled are removed from ${to_bundle} list.
# Other libraries found but not listed in ${to_bundle} are returned
# in ${ignored} variable.
# If additional arguments are given, they are used as glob expressions to find
# the libraries to be bundled, otherwise 3rd parties bundled in with the server
# are searched in ${MYSQL_LIB_DIR} locations.

macro(bundle_libs to_bundle ignored)

  #message("== bundle_libs: ${${to_bundle}}")

  if(ARGN)

   file(GLOB _bundled ${ARGN})

  else()

    file(GLOB _bundled
      "${MYSQL_LIB_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}*"
      "${MYSQL_LIB_DIR}/private/*${CMAKE_SHARED_LIBRARY_SUFFIX}*"
    )

    # On windows, libs are in bin directory

    if(WIN32)
      file(GLOB _bundled1 "${MYSQL_DIR}/bin/*${CMAKE_SHARED_LIBRARY_SUFFIX}*")
    endif()

  endif()

  # Process collected libraries
  set(libs ${${to_bundle}})

  foreach(lib IN LISTS _bundled _bundled1)

    get_filename_component(lib_name ${lib} NAME)
    #message("== looking at 3rd party lib: ${lib_name} (${bundled_${lib_name}})")

    # Match library name against names in to_bundle list and in case of match remove that name from the list

    unset(found)
    foreach(name ${libs})

      #message(STATUS "checking lib ${pos}: ${name}")

      if(
        (lib_name MATCHES "^${name}")
        OR ((NOT WIN32) AND (lib_name MATCHES "^lib${name}"))
      )
        #message(STATUS "removing from list: ${name}")
        list(REMOVE_ITEM ${to_bundle} ${name})
        bundle_lib(${lib})
        set(found ${name})
        break()
      endif()

    endforeach()

    if(NOT found)
      #message(STATUS "ignoring it (${lib_name})")
      list(APPEND ${ignored} ${lib_name})
    endif()

  endforeach()

endmacro(bundle_libs)


# Bundle plugins listed in PLUGINS list. Each bundled plugin P is removed from
# the list and its dependedencies listed in DEPS_${P} are also bundled. Client
# side plugins found with the server and not listed in PLUGINS are returned
# in ${ignored} list.

macro(bundle_plugins ignored)

  #message("== PLUGINS: ${PLUGINS}")

  file(GLOB _bundled "${MYSQL_PLUGIN_DIR}/*${CMAKE_SHARED_MODULE_SUFFIX}*")

  set(plugins ${PLUGINS})

  foreach(lib ${_bundled})

    get_filename_component(lib_name ${lib} NAME_WE)

    if(NOT lib_name MATCHES "_client")
      continue()
    endif()

    #message("== looking at client-side plugin: ${lib_name}")

    # Match plugin name against names in PLUGINS list and in case of match
    # remove that name from the list

    unset(plugin)
    foreach(check ${plugins})

      #message(STATUS "checking plugin ${pos}: ${check}")

      if(
        lib_name MATCHES "^${check}"
      )
        #message(STATUS "plugin name match")
        set(plugin ${check})
        list(REMOVE_ITEM PLUGINS ${plugin})
        break()
      endif()

    endforeach()

    if(NOT plugin)
      #message(STATUS "ignoring it")
      list(APPEND ${ignored} ${lib_name})
      continue()
    endif()

    message("Bundling client plugin: ${lib}")

    install(FILES "${lib}"
      CONFIGURATIONS Release RelWithDebInfo
      DESTINATION "${LIB_SUBDIR}/plugin"
      COMPONENT ODBCDll
    )

    # See if libsasl is bundled to also bundle sasl plugins
    if("${DEPS_${plugin}}" MATCHES "sasl")
      set(sasl_bundled 1)
    endif()

    # On Windows the MIT Kerberos library uses ccapiserver.exe application
    # in some scenarios - we need to bundle it as well.

    if(WIN32 AND "${DEPS_${plugin}}" MATCHES "krb5")
      file(GLOB ccapiserver "${MYSQL_DIR}/bin/ccapiserver.exe")
      if(ccapiserver)
        bundle_lib("${ccapiserver}")
      elseif(MAINTAINER_MODE)
        message(SEND_ERROR
          "Missing ccapiserver executable required by Kerberos"
          " authentication plugin"
        )
      endif()
    endif()

    # Bundle plugin dependencies

    bundle_libs(DEPS_${plugin} ignored_deps)

    # In MAINTAINER_MODE report error if some plugin dependencies were not found

    if(DEPS_${plugin})

      if("${DEPS_${plugin}}" MATCHES "sasl")
        #message(STATUS "required SASL dependency not found, skipping SASL plugins")
        set(sasl_bundled 0)
      endif()

      if(MAINTAINER_MODE)
        message(SEND_ERROR
          "The following dependencies of bundled plugin ${plugin}"
          " were not found: ${DEPS_${plugin}}"
        )
      endif()

    endif()

  endforeach()

  # Also bundle modules required by the SASL library if they exist
  # and the library was bundled.

  if(
    sasl_bundled AND NOT bundled_sasl_modules
    AND EXISTS "${MYSQL_LIB_DIR}/private/sasl2"
  )

    message("Bundling SASL modules")
    set(bundled_sasl_modules true)
    install(DIRECTORY "${MYSQL_LIB_DIR}/private/sasl2"
      CONFIGURATIONS Release RelWithDebInfo
      DESTINATION "${LIB_SUBDIR}/private"
      COMPONENT OdbcDll #FIXME
    )

  endif()

endmacro(bundle_plugins)


if(BUNDLE_DEPENDENCIES)

  message(STATUS "Looking for bundled client-side plugins")

  # add authentication plugins to the list of plugins

  foreach(auth ${AUTH_PLUGINS})
    set(plugin "authentication_${auth}")
    list(APPEND PLUGINS ${plugin})
    set(DEPS_${plugin} ${AUTH_DEPS_${auth}})
    list(APPEND IGNORED_LIBS ${DEPS_${plugin}})
  endforeach()

  # Bundle the plugins and their dependencies.

  unset(ingored)
  bundle_plugins(ignored)

  if(MAINTAINER_MODE)

    foreach(plugin ${PLUGINS})
      message(SEND_ERROR "known client-side plugin not found and not bundled: ${plugin}")
    endforeach()

    foreach(plugin ${IGNORED_PLUGINS})

      # Note: Old cmake (seen with 3.13.4) generates this error on
      # list(FILTER...) if the list is empty:
      # "list sub-command FILTER requires list to be present"

      if(NOT ignored)
        break()
      endif()

      list(FILTER ignored EXCLUDE REGEX "^${plugin}")

    endforeach()

    if(ignored)
      message(SEND_ERROR
        "Unrecognized client-side plugins bundled with the server were found,"
        " please update definitions in cmake lists to either ignore them"
        " or bundle with the conector: ${ignored}"
      )
    endif()

  endif(MAINTAINER_MODE)


  message(STATUS "Looking for bundled client lib dependencies")

  # Bundle additional libraries listed in BUNDLED_LIBS
  # For OpenSSL libs, first look in the location of the library that
  # is actually being used in the build.

  #message("== BUNDLED_LIBS: ${BUNDLED_LIBS}")
  #message("== IGNORED_LIBS: ${IGNORED_LIBS}")

  get_filename_component(OPENSSL_DIR ${OPENSSL_LIBRARY} DIRECTORY)

  bundle_libs(BUNDLED_LIBS extra_libs
   "${OPENSSL_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}*"
  )
  unset(extra_libs)
  bundle_libs(BUNDLED_LIBS extra_libs)

  # In MAINTAINER_MODE check whether all 3rd party libs found but not bundled
  # are listed in IGNORED_LIBS

  if(MAINTAINER_MODE)

    # Extend ignore list with libraries that are dependencies of known plugins
    # and are not listed in BUNDLED_LIBS. Otherwise we would get false errors
    # below.

    foreach(plugin PLUGINS)
      list(APPEND IGNORED_LIBS ${DEPS_${plugin}})
    endforeach()

    # Remove from ${extra_libs} the libraries that we know we should ignore.
    # Also the openssl libs that might end up in ${extra_libs} bacause of two
    # stage search logic above.

    #message("== extra_libs: ${extra_libs}")
    foreach(lib ${IGNORED_LIBS} libssl libcrypto ssleay libeay)
      #message(STATUS "removing: ${lib}")
      list(FILTER extra_libs EXCLUDE REGEX "^${lib}")
      list(FILTER extra_libs EXCLUDE REGEX "^lib${lib}")
    endforeach()
    #message("== extra_libs: ${extra_libs}")

    if(extra_libs)
      message(SEND_ERROR
        "Unrecognized 3rd party library bundled with the server were found,"
        " please update definitions in cmake lists to either ignore them"
        " or bundle with the conector: ${extra_libs}"
      )
    endif()

  endif()

endif(BUNDLE_DEPENDENCIES)


INCLUDE(CPack)
