cmake_minimum_required(VERSION 3.16)

project(matrixssl VERSION 4.6.0 LANGUAGES C)

include(GNUInstallDirs)

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
    set(MATRIXSSL_TOP_LEVEL_BUILD TRUE)
else()
    set(MATRIXSSL_TOP_LEVEL_BUILD FALSE)
endif()

if(MATRIXSSL_TOP_LEVEL_BUILD)
    include(CTest)
elseif(NOT DEFINED BUILD_TESTING)
    set(BUILD_TESTING OFF)
endif()

if(MATRIXSSL_TOP_LEVEL_BUILD)
    set(MATRIXSSL_DEFAULT_BUILD_PROGRAMS ON)
else()
    set(MATRIXSSL_DEFAULT_BUILD_PROGRAMS OFF)
endif()

option(MATRIXSSL_BUILD_SHARED "Build MatrixSSL as a shared library" ON)
option(MATRIXSSL_BUILD_STATIC "Build MatrixSSL as a static library" OFF)
option(MATRIXSSL_DISABLE_TLS13 "Disable TLS 1.3 in the generated CMake configuration" OFF)
option(MATRIXSSL_BUILD_TESTS "Build MatrixSSL test programs matching the GNUmakefile tests target" ${MATRIXSSL_DEFAULT_BUILD_PROGRAMS})
option(MATRIXSSL_BUILD_APPS "Build MatrixSSL example applications matching the GNUmakefile apps target" ${MATRIXSSL_DEFAULT_BUILD_PROGRAMS})
option(MATRIXSSL_BUILD_TOOLS "Build auxiliary tools available from the legacy makefiles" ${MATRIXSSL_DEFAULT_BUILD_PROGRAMS})
option(MATRIXSSL_USE_SODIUM "Link MatrixSSL against libsodium for Ed25519/Curve25519 helper code" ON)
set(MATRIXSSL_CONFIG "default" CACHE STRING "MatrixSSL configuration directory under configs/")
set(MATRIXSSL_LIBRARY_PREFIX "" CACHE STRING "Prefix added to installed library output names, e.g. 'nw' for libnwmatrixssl")
set(MATRIXSSL_PROGRAM_PREFIX "${MATRIXSSL_LIBRARY_PREFIX}" CACHE STRING "Prefix added to test, app, and tool executable/module output names. Defaults to MATRIXSSL_LIBRARY_PREFIX")
set(MATRIXSSL_INSTALL_INCLUDE_SUBDIR "" CACHE STRING "Header install subdirectory below CMAKE_INSTALL_INCLUDEDIR. Empty derives from MATRIXSSL_LIBRARY_PREFIX: matrixssl or <prefix>matrixssl")
if(MATRIXSSL_INSTALL_INCLUDE_SUBDIR STREQUAL "")
    if(MATRIXSSL_LIBRARY_PREFIX STREQUAL "")
        set(MATRIXSSL_INSTALL_INCLUDE_SUBDIR "matrixssl")
    else()
        set(MATRIXSSL_INSTALL_INCLUDE_SUBDIR "${MATRIXSSL_LIBRARY_PREFIX}matrixssl")
    endif()
endif()
set(MATRIXSSL_PROGRAM_INSTALL_DIR "${CMAKE_INSTALL_LIBEXECDIR}/${MATRIXSSL_INSTALL_INCLUDE_SUBDIR}" CACHE STRING "Install directory for MatrixSSL apps/tests/tools")
set(MATRIXSSL_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
set(MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR "${MATRIXSSL_BUILD_INCLUDE_DIR}/${MATRIXSSL_INSTALL_INCLUDE_SUBDIR}")
set(MATRIXSSL_BUILD_OPENSSL_INCLUDE_DIR "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/openssl")

set(MATRIXSSL_CONFIG_DIR "${CMAKE_CURRENT_SOURCE_DIR}/configs/${MATRIXSSL_CONFIG}")
if(NOT EXISTS "${MATRIXSSL_CONFIG_DIR}/cryptoConfig.h" OR
   NOT EXISTS "${MATRIXSSL_CONFIG_DIR}/matrixsslConfig.h" OR
   NOT EXISTS "${MATRIXSSL_CONFIG_DIR}/coreConfig.h")
    message(FATAL_ERROR "Unknown or incomplete MATRIXSSL_CONFIG='${MATRIXSSL_CONFIG}'")
endif()

set(MATRIXSSL_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
file(MAKE_DIRECTORY
    "${MATRIXSSL_GENERATED_DIR}/crypto"
    "${MATRIXSSL_GENERATED_DIR}/matrixssl"
    "${MATRIXSSL_GENERATED_DIR}/core/config")
configure_file("${MATRIXSSL_CONFIG_DIR}/cryptoConfig.h"
    "${MATRIXSSL_GENERATED_DIR}/crypto/cryptoConfig.h" COPYONLY)
configure_file("${MATRIXSSL_CONFIG_DIR}/coreConfig.h"
    "${MATRIXSSL_GENERATED_DIR}/core/config/coreConfig.h" COPYONLY)
file(READ "${MATRIXSSL_CONFIG_DIR}/matrixsslConfig.h" MATRIXSSL_CONFIG_TEXT)
if(MATRIXSSL_DISABLE_TLS13)
    string(APPEND MATRIXSSL_CONFIG_TEXT "\n/* Added by CMake build: keep MatrixSSL 4.6.0 TLS 1.2-only by default. */\n#ifndef DISABLE_TLS_1_3\n#define DISABLE_TLS_1_3\n#endif\n")
endif()
file(WRITE "${MATRIXSSL_GENERATED_DIR}/matrixssl/matrixsslConfig.h" "${MATRIXSSL_CONFIG_TEXT}")

if(MATRIXSSL_USE_SODIUM)
    if(NOT TARGET sodium::sodium)
        find_package(sodium CONFIG QUIET)
    endif()
    if(NOT TARGET sodium::sodium AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../libsodium/CMakeLists.txt")
        add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../libsodium"
            "${CMAKE_CURRENT_BINARY_DIR}/../libsodium")
    endif()
    if(NOT TARGET sodium::sodium)
        message(FATAL_ERROR "MATRIXSSL_USE_SODIUM=ON but sodium::sodium was not found. Add third_party/libsodium first or install the sodium CMake package.")
    endif()
endif()

file(MAKE_DIRECTORY
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}"
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/crypto"
    "${MATRIXSSL_BUILD_OPENSSL_INCLUDE_DIR}")
configure_file("${MATRIXSSL_GENERATED_DIR}/core/config/coreConfig.h"
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/coreConfig.h" COPYONLY)
configure_file("${MATRIXSSL_GENERATED_DIR}/crypto/cryptoConfig.h"
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/cryptoConfig.h" COPYONLY)
configure_file("${MATRIXSSL_GENERATED_DIR}/crypto/cryptoConfig.h"
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/crypto/cryptoConfig.h" COPYONLY)
configure_file("${MATRIXSSL_GENERATED_DIR}/matrixssl/matrixsslConfig.h"
    "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/matrixsslConfig.h" COPYONLY)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/core/include/"
    DESTINATION "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}"
    FILES_MATCHING PATTERN "*.h")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/core/osdep/include/"
    DESTINATION "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}"
    FILES_MATCHING PATTERN "*.h")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/crypto/"
    DESTINATION "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/crypto"
    FILES_MATCHING PATTERN "*.h")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/matrixssl/"
    DESTINATION "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}"
    FILES_MATCHING PATTERN "*.h")
set(MATRIXSSL_COMMON_INCLUDE_DIRS
    "${MATRIXSSL_GENERATED_DIR}/core/config"
    "${MATRIXSSL_GENERATED_DIR}/crypto"
    "${MATRIXSSL_GENERATED_DIR}/matrixssl"
    "${CMAKE_CURRENT_SOURCE_DIR}/crypto/aead/chacha20poly1305ietf"
    "${CMAKE_CURRENT_SOURCE_DIR}/crypto/scalarmult/include/sodium"
    "${CMAKE_CURRENT_SOURCE_DIR}/crypto/crypto_sign/include/sodium"
    "${CMAKE_CURRENT_SOURCE_DIR}"
    "${CMAKE_CURRENT_SOURCE_DIR}/core/include"
    "${CMAKE_CURRENT_SOURCE_DIR}/core/config"
    "${CMAKE_CURRENT_SOURCE_DIR}/core/include/sfzcl"
    "${CMAKE_CURRENT_SOURCE_DIR}/core/osdep/include"
    "${CMAKE_CURRENT_SOURCE_DIR}/crypto"
    "${CMAKE_CURRENT_SOURCE_DIR}/matrixssl")

set(MATRIXSSL_CORE_SOURCES
    core/src/memset_s.c
    core/src/corelib_main.c
    core/src/corelib_trace.c
    core/src/corelib_date.c
    core/src/corelib_strings.c
    core/src/corelib_list.c
    core/src/psbuf.c
    core/src/psUtil.c
    core/src/psStat.c
    core/osdep/POSIX/osdep.c
    core/osdep/ANSI/osdep_break.c
    core/osdep/POSIX/psLog.c
    core/osdep/POSIX/psPrnf.c
    core/src/c_lib.c
    core/src/cl_basic.c
    core/src/debug_abort.c
    core/src/debug_printf.c
    core/src/psprintf.c
    core/src/psmalloc.c
    core/src/psmalloc_ext.c
    core/src/sfzclbuffer.c
    core/src/sfzclcalendar.c
    core/src/sfzclfastalloc.c
    core/src/sfzclfileio.c
    core/src/sfzclmalloc.c
    core/src/sfzclmemparser.c
    core/src/sfzcltimemeasure.c
    core/src/sfzclobstack.c
    core/src/sfzclsnprintf.c
    core/src/sfzclbase64.c
    core/src/sfzclstr.c
    core/src/sfzcltime.c
    core/osdep/ANSI/spal_memory_ansi.c
    core/src/utils.c
    core/osdep/POSIX/spal_posix_mutex.c
    core/osdep/POSIX/spal_posix_semaphore.c
    core/osdep/POSIX/spal_posix_sleep.c
    core/osdep/POSIX/spal_posix_thread.c
    core/src/sl_cpu.c
    core/src/sl_neon.c
    core/osdep/src/cl_memset.c
    core/osdep/src/runtime.c)

set(MATRIXSSL_CRYPTO_SOURCES
    crypto/common/alg_info.c
    crypto/common/digest_info.c
    crypto/symmetric/aes.c
    crypto/symmetric/aesCBC.c
    crypto/symmetric/aesGCM.c
    crypto/symmetric/aes_aesni.c
    crypto/symmetric/arc4.c
    crypto/symmetric/des3.c
    crypto/symmetric/idea.c
    crypto/symmetric/rc2.c
    crypto/symmetric/seed.c
    crypto/digest/hash.c
    crypto/digest/sha256_standalone.c
    crypto/digest/sha1.c
    crypto/digest/sha256.c
    crypto/digest/sha512.c
    crypto/digest/md5sha1.c
    crypto/digest/md5.c
    crypto/digest/hmac.c
    crypto/digest/md4.c
    crypto/digest/md2.c
    crypto/digest/hkdf.c
    crypto/keyformat/asn1.c
    crypto/keyformat/asn1fmt.c
    crypto/keyformat/base64.c
    crypto/keyformat/crl.c
    crypto/keyformat/pem_decode_mem.c
    crypto/keyformat/pem_decode_file.c
    crypto/keyformat/pkcs.c
    crypto/keyformat/pbkdf2.c
    crypto/keyformat/x509.c
    crypto/layer/matrix.c
    crypto/math/pstm.c
    crypto/math/pstmnt.c
    crypto/math/pstm_montgomery_reduce.c
    crypto/math/pstm_mul_comba.c
    crypto/math/pstm_sqr_comba.c
    crypto/prng/prng.c
    crypto/prng/yarrow.c
    crypto/pubkey/dh.c
    crypto/pubkey/dh_params.c
    crypto/pubkey/dh_export.c
    crypto/pubkey/dh_import.c
    crypto/pubkey/dh_gen_key.c
    crypto/pubkey/dh_gen_secret.c
    crypto/pubkey/dh_import_priv.c
    crypto/pubkey/ecc.c
    crypto/pubkey/ecc_curve.c
    crypto/pubkey/ecc_curve_data.c
    crypto/pubkey/ecc_curve_config.c
    crypto/pubkey/ecc_math.c
    crypto/pubkey/ecc_priv.c
    crypto/pubkey/ecc_priv_el_gamal.c
    crypto/pubkey/ecc_pub.c
    crypto/pubkey/ecc_keygen.c
    crypto/pubkey/ecc_gen_shared.c
    crypto/pubkey/ecc_parse_file.c
    crypto/pubkey/ecc_parse_mem.c
    crypto/pubkey/ecc_write_mem.c
    crypto/pubkey/ecc_write_file.c
    crypto/pubkey/ecc_export.c
    crypto/pubkey/ecc_import.c
    crypto/pubkey/pubkey.c
    crypto/pubkey/pubkey_sign.c
    crypto/pubkey/pubkey_verify.c
    crypto/pubkey/pubkey_parse_mem.c
    crypto/pubkey/pubkey_parse_file.c
    crypto/pubkey/rsa.c
    crypto/pubkey/rsa_priv.c
    crypto/pubkey/rsa_pub.c
    crypto/pubkey/rsa_keygen.c
    crypto/pubkey/rsa_parse_mem.c
    crypto/pubkey/rsa_parse_file.c
    crypto/pubkey/rsa_write_mem.c
    crypto/pubkey/rsa_write_file.c
    crypto/aead/chacha20poly1305ietf/sse2/poly1305_sse2.c
    crypto/aead/chacha20poly1305ietf/donna/poly1305_donna.c
    crypto/aead/chacha20poly1305ietf/dolbeau/chacha20_dolbeau-avx2.c
    crypto/aead/chacha20poly1305ietf/dolbeau/chacha20_dolbeau-ssse3.c
    crypto/aead/chacha20poly1305ietf/ref/chacha20_ref.c
    crypto/aead/chacha20poly1305ietf/stream_chacha20.c
    crypto/aead/chacha20poly1305ietf/onetimeauth_poly1305.c
    crypto/aead/chacha20poly1305ietf/ps_chacha20poly1305ietf.c
    crypto/aead/chacha20poly1305ietf/aead_chacha20poly1305.c
    crypto/aead/chacha20poly1305ietf/utils.c
    crypto/aead/chacha20poly1305ietf/verify.c
    crypto/aead/chacha20poly1305ietf/runtime.c
    crypto/scalarmult/curve25519/scalarmult_curve25519.c
    crypto/scalarmult/curve25519/ref10/x25519_ref10.c
    crypto/scalarmult/ed25519/ref10/scalarmult_ed25519_ref10.c
    crypto/scalarmult/crypto_scalarmult.c
    crypto/scalarmult/crypto_core/ed25519/core_ed25519.c
    crypto/scalarmult/crypto_core/ed25519/ref10/ed25519_ref10.c
    crypto/scalarmult/ps_x25519.c
    crypto/crypto_sign/crypto_sign.c
    crypto/crypto_sign/ed25519/ref10/sign.c
    crypto/crypto_sign/ed25519/ref10/open.c
    crypto/crypto_sign/ed25519/ref10/keypair.c
    crypto/crypto_sign/ps_ed25519.c)

set(MATRIXSSL_TLS_SOURCES
    matrixssl/matrixsslGetSet.c
    matrixssl/cipherSuite.c
    matrixssl/dtls.c
    matrixssl/extDecode.c
    matrixssl/hsDecode.c
    matrixssl/hsHash.c
    matrixssl/hsHashBuffered.c
    matrixssl/hsNegotiateVersion.c
    matrixssl/matrixssl.c
    matrixssl/matrixsslKeys.c
    matrixssl/matrixsslApi.c
    matrixssl/matrixsslInitVer.c
    matrixssl/matrixsslSecConfig.c
    matrixssl/prf.c
    matrixssl/psk.c
    matrixssl/sslDecode.c
    matrixssl/sslEncode.c
    matrixssl/tlsDefaults.c
    matrixssl/tlsSelectKeys.c
    matrixssl/tlsSigVer.c
    matrixssl/tlsTrace.c
    matrixssl/tls13Adapter.c
    matrixssl/tls13Authenticate.c
    matrixssl/tls13CipherSuite.c
    matrixssl/tls13Encode.c
    matrixssl/tls13EncodeExt.c
    matrixssl/tls13Decode.c
    matrixssl/tls13DecodeExt.c
    matrixssl/tls13KeyAgree.c
    matrixssl/tls13KeySchedule.c
    matrixssl/tls13Negotiate.c
    matrixssl/tls13Psk.c
    matrixssl/tls13Resume.c
    matrixssl/tls13SigVer.c
    matrixssl/tls13TrHash.c
    matrixssl/tls13TrHashBuffered.c
    matrixssl/sslv3.c
    matrixssl/tls.c)

if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64|i[3-6]86)$")
    set_source_files_properties(crypto/symmetric/aes_aesni.c
        PROPERTIES COMPILE_OPTIONS "-maes;-msse4.1;-mpclmul")
    set_source_files_properties(crypto/aead/chacha20poly1305ietf/sse2/poly1305_sse2.c
        PROPERTIES COMPILE_OPTIONS "-msse2")
    set_source_files_properties(crypto/aead/chacha20poly1305ietf/dolbeau/chacha20_dolbeau-ssse3.c
        PROPERTIES COMPILE_OPTIONS "-mssse3")
    set_source_files_properties(crypto/aead/chacha20poly1305ietf/dolbeau/chacha20_dolbeau-avx2.c
        PROPERTIES COMPILE_OPTIONS "-mavx2")
endif()

set(MATRIXSSL_ALL_SOURCES
    ${MATRIXSSL_CORE_SOURCES}
    ${MATRIXSSL_CRYPTO_SOURCES}
    ${MATRIXSSL_TLS_SOURCES})
function(matrixssl_configure_target target_name)
    target_sources(${target_name} PRIVATE ${MATRIXSSL_ALL_SOURCES})
    target_include_directories(${target_name}
        PRIVATE
            ${MATRIXSSL_COMMON_INCLUDE_DIRS}
        INTERFACE
            $<BUILD_INTERFACE:${MATRIXSSL_BUILD_INCLUDE_DIR}>
            $<BUILD_INTERFACE:${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${MATRIXSSL_INSTALL_INCLUDE_SUBDIR}>)
    target_compile_features(${target_name} PRIVATE c_std_99)
    target_compile_definitions(${target_name} PUBLIC MATRIX_CONFIGURATION_INCDIR_FIRST)
    target_compile_options(${target_name} PRIVATE -ffunction-sections -fdata-sections -fno-math-errno)
    if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64|i[3-6]86)$")
        # MatrixSSL builds the library with AES enabled on x86 so
        # PSCRYPTO_CONFIG contains AESNI=Y.  Consumers that include
        # cryptoApi.h must see the same compiler feature macros, otherwise
        # psCryptoOpen() rejects them with a crypto config mismatch.
        target_compile_options(${target_name} PUBLIC -maes)
    endif()
    if(UNIX AND NOT APPLE)
        target_link_libraries(${target_name} PUBLIC pthread m)
    endif()
    if(MATRIXSSL_USE_SODIUM)
        target_link_libraries(${target_name} PUBLIC sodium::sodium)
    endif()
    set_target_properties(${target_name} PROPERTIES
        OUTPUT_NAME "${MATRIXSSL_LIBRARY_PREFIX}matrixssl"
        VERSION "${PROJECT_VERSION}"
        SOVERSION "${PROJECT_VERSION_MAJOR}")
endfunction()

if(MATRIXSSL_BUILD_SHARED)
    add_library(matrixssl SHARED)
    matrixssl_configure_target(matrixssl)
    add_library(MATRIXSSL::matrixssl ALIAS matrixssl)
endif()

if(MATRIXSSL_BUILD_STATIC)
    add_library(matrixssl_static STATIC)
    matrixssl_configure_target(matrixssl_static)
    set_target_properties(matrixssl_static PROPERTIES OUTPUT_NAME "${MATRIXSSL_LIBRARY_PREFIX}matrixssl")
    if(NOT MATRIXSSL_BUILD_SHARED)
        add_library(MATRIXSSL::matrixssl ALIAS matrixssl_static)
    endif()
endif()

if((MATRIXSSL_BUILD_TESTS OR MATRIXSSL_BUILD_APPS) AND NOT TARGET matrixssl_static)
    add_library(matrixssl_static STATIC EXCLUDE_FROM_ALL)
    matrixssl_configure_target(matrixssl_static)
    set_target_properties(matrixssl_static PROPERTIES OUTPUT_NAME "${MATRIXSSL_LIBRARY_PREFIX}matrixssl")
endif()


function(matrixssl_configure_program target_name)
    target_include_directories(${target_name}
        PRIVATE
            ${MATRIXSSL_COMMON_INCLUDE_DIRS}
            "${CMAKE_CURRENT_SOURCE_DIR}/apps/common"
            "${CMAKE_CURRENT_SOURCE_DIR}/apps/ssl"
            "${CMAKE_CURRENT_SOURCE_DIR}/apps/dtls"
            "${CMAKE_CURRENT_SOURCE_DIR}/core/include/testsupp")
    target_compile_definitions(${target_name} PRIVATE MATRIX_CONFIGURATION_INCDIR_FIRST)
    if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64|i[3-6]86)$")
        target_compile_options(${target_name} PRIVATE -maes)
    endif()
    if(UNIX AND NOT APPLE)
        target_link_libraries(${target_name} PRIVATE pthread m)
    endif()
endfunction()

function(matrixssl_link_program target_name)
    matrixssl_configure_program(${target_name})
    if(TARGET matrixssl_static)
        target_link_libraries(${target_name} PRIVATE matrixssl_static)
    else()
        target_link_libraries(${target_name} PRIVATE matrixssl)
    endif()
endfunction()

function(matrixssl_prefixed_output_name output_var target_name)
    string(REGEX REPLACE "^matrixssl_" "" _matrixssl_output_name "${target_name}")
    set(${output_var} "${MATRIXSSL_PROGRAM_PREFIX}${_matrixssl_output_name}" PARENT_SCOPE)
endfunction()

function(matrixssl_add_test_program target_name)
    add_executable(${target_name} ${ARGN})
    matrixssl_link_program(${target_name})
    matrixssl_prefixed_output_name(_matrixssl_test_output_name ${target_name})
    set_target_properties(${target_name} PROPERTIES
        OUTPUT_NAME "${_matrixssl_test_output_name}"
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests")
endfunction()

function(matrixssl_add_app_program target_name)
    add_executable(${target_name} ${ARGN})
    matrixssl_link_program(${target_name})
    matrixssl_prefixed_output_name(_matrixssl_app_output_name ${target_name})
    set_target_properties(${target_name} PROPERTIES
        OUTPUT_NAME "${_matrixssl_app_output_name}"
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/apps")
endfunction()

if(MATRIXSSL_BUILD_TESTS)
    add_custom_target(matrixssl_tests ALL)

    matrixssl_add_test_program(matrixssl_algorithmTest crypto/test/algorithmTest.c)
    matrixssl_add_test_program(matrixssl_throughputTest crypto/test/throughputTest.c)
    matrixssl_add_test_program(matrixssl_cryptoOpen crypto/test/cryptoOpen.c)
    matrixssl_add_test_program(matrixssl_eccTest crypto/test/eccTest.c)
    matrixssl_add_test_program(matrixssl_rsaTest crypto/test/rsaTest.c)
    matrixssl_add_test_program(matrixssl_hmacTest crypto/test/hmacTest.c)
    matrixssl_add_test_program(matrixssl_rsaperf crypto/test/rsaperf/rsaperf.c)
    matrixssl_add_test_program(matrixssl_eccperf crypto/test/eccperf/eccperf.c)
    matrixssl_add_test_program(matrixssl_dhperf crypto/test/dhperf/dhperf.c)

    matrixssl_add_test_program(matrixssl_sslTest matrixssl/test/sslTest.c)
    matrixssl_add_test_program(matrixssl_certValidate matrixssl/test/certValidate.c)
    matrixssl_add_test_program(matrixssl_pkcs12Test matrixssl/test/pkcs12Test.c)
    target_compile_definitions(matrixssl_pkcs12Test PRIVATE USE_CL_PKCS USE_CL_CERTLIB)

    add_dependencies(matrixssl_tests
        matrixssl_algorithmTest matrixssl_throughputTest matrixssl_cryptoOpen
        matrixssl_eccTest matrixssl_rsaTest matrixssl_hmacTest
        matrixssl_rsaperf matrixssl_eccperf matrixssl_dhperf
        matrixssl_sslTest matrixssl_certValidate matrixssl_pkcs12Test)

    if(BUILD_TESTING)
        add_test(NAME cryptoOpen COMMAND matrixssl_cryptoOpen)
        add_test(NAME eccTest COMMAND matrixssl_eccTest)
        add_test(NAME rsaTest COMMAND matrixssl_rsaTest)
        add_test(NAME hmacTest COMMAND matrixssl_hmacTest)
        set_tests_properties(cryptoOpen eccTest rsaTest hmacTest PROPERTIES
            WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
    endif()
endif()

if(MATRIXSSL_BUILD_APPS)
    add_custom_target(matrixssl_apps ALL)

    add_library(matrixssl_client_common STATIC
        apps/common/client_common.c
        apps/common/clientconfig.c
        apps/common/load_keys.c)
    matrixssl_link_program(matrixssl_client_common)
    target_compile_definitions(matrixssl_client_common PRIVATE ID_RSA)
    matrixssl_prefixed_output_name(_matrixssl_client_common_output_name matrixssl_client_common)
    set_target_properties(matrixssl_client_common PROPERTIES
        OUTPUT_NAME "${_matrixssl_client_common_output_name}"
        ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/apps")

    matrixssl_add_app_program(matrixssl_server apps/ssl/server.c apps/ssl/http.c)
    matrixssl_add_app_program(matrixssl_client apps/ssl/client.c apps/ssl/http.c)
    target_link_libraries(matrixssl_client PRIVATE matrixssl_client_common)
    matrixssl_add_app_program(matrixssl_simpleClient apps/ssl/simpleClient.c)
    matrixssl_add_app_program(matrixssl_simpleServer apps/ssl/simpleServer.c)
    matrixssl_add_app_program(matrixssl_interactiveClient apps/ssl/interactiveClient.c apps/ssl/interactiveCommon.c)
    matrixssl_add_app_program(matrixssl_interactiveServer apps/ssl/interactiveServer.c apps/ssl/interactiveCommon.c)
    matrixssl_add_app_program(matrixssl_tlsDtlsServer apps/ssl/tlsDtlsServer.c apps/ssl/http.c)

    matrixssl_add_app_program(matrixssl_dtlsServer apps/dtls/dtlsServer.c apps/dtls/dtlsCommon.c)
    matrixssl_add_app_program(matrixssl_dtlsClient apps/dtls/dtlsClient.c apps/dtls/dtlsCommon.c)
    target_link_libraries(matrixssl_dtlsClient PRIVATE matrixssl_client_common)

    foreach(_matrixssl_app IN ITEMS
        matrixssl_client_common matrixssl_server matrixssl_client
        matrixssl_simpleClient matrixssl_simpleServer
        matrixssl_interactiveClient matrixssl_interactiveServer
        matrixssl_tlsDtlsServer
        matrixssl_dtlsServer matrixssl_dtlsClient)
        target_compile_definitions(${_matrixssl_app} PRIVATE ID_RSA)
    endforeach()

    add_dependencies(matrixssl_apps
        matrixssl_server matrixssl_client matrixssl_simpleClient matrixssl_simpleServer
        matrixssl_interactiveClient matrixssl_interactiveServer matrixssl_tlsDtlsServer
        matrixssl_dtlsServer matrixssl_dtlsClient)
endif()

if(MATRIXSSL_BUILD_TOOLS)
    add_custom_target(matrixssl_tools ALL)

    if(UNIX AND NOT APPLE)
        add_library(matrixssl_wrap_malloc MODULE core/wrapper/wrap-malloc.c)
        set_target_properties(matrixssl_wrap_malloc PROPERTIES
            PREFIX ""
            OUTPUT_NAME "${MATRIXSSL_PROGRAM_PREFIX}wrap-malloc"
            LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tools")
        target_compile_options(matrixssl_wrap_malloc PRIVATE -Wall -Werror)
        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tools")
        file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/tools/wrap-malloc-include.txt"
            "EXTRA_CFLAGS=\"-include ${CMAKE_CURRENT_SOURCE_DIR}/core/wrapper/wrap-malloc.h\" EXTRA_LDFLAGS=\"${CMAKE_CURRENT_BINARY_DIR}/tools/${MATRIXSSL_PROGRAM_PREFIX}wrap-malloc.so\"\n")
        add_dependencies(matrixssl_tools matrixssl_wrap_malloc)
    endif()
endif()


if(MATRIXSSL_BUILD_TESTS)
    install(TARGETS
        matrixssl_algorithmTest matrixssl_throughputTest matrixssl_cryptoOpen
        matrixssl_eccTest matrixssl_rsaTest matrixssl_hmacTest
        matrixssl_rsaperf matrixssl_eccperf matrixssl_dhperf
        matrixssl_sslTest matrixssl_certValidate matrixssl_pkcs12Test
        RUNTIME DESTINATION ${MATRIXSSL_PROGRAM_INSTALL_DIR}
        OPTIONAL)
endif()
if(MATRIXSSL_BUILD_APPS)
    install(TARGETS
        matrixssl_server matrixssl_client matrixssl_simpleClient matrixssl_simpleServer
        matrixssl_interactiveClient matrixssl_interactiveServer matrixssl_tlsDtlsServer
        matrixssl_dtlsServer matrixssl_dtlsClient
        RUNTIME DESTINATION ${MATRIXSSL_PROGRAM_INSTALL_DIR}
        OPTIONAL)
endif()
if(MATRIXSSL_BUILD_TOOLS AND TARGET matrixssl_wrap_malloc)
    install(TARGETS matrixssl_wrap_malloc
        LIBRARY DESTINATION ${MATRIXSSL_PROGRAM_INSTALL_DIR})
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tools/wrap-malloc-include.txt"
        DESTINATION ${MATRIXSSL_PROGRAM_INSTALL_DIR})
endif()

if(MATRIXSSL_BUILD_SHARED)
    install(TARGETS matrixssl EXPORT matrixsslTargets
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if(MATRIXSSL_BUILD_STATIC)
    install(TARGETS matrixssl_static EXPORT matrixsslTargets
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

install(DIRECTORY "${MATRIXSSL_BUILD_PUBLIC_INCLUDE_DIR}/"
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${MATRIXSSL_INSTALL_INCLUDE_SUBDIR}
    FILES_MATCHING PATTERN "*.h")
install(EXPORT matrixsslTargets
    NAMESPACE MATRIXSSL::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/matrixssl)
