cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(cwrapper LANGUAGES CXX)

if(APPLE)
    set(LIB_DIR "mac_")
elseif(UNIX)
    set(LIB_DIR "linux_")
elseif(WIN32)
    set(LIB_DIR "windows_")
else()
    message(FATAL_ERROR "Unsupported operating system!")
endif()

if(CMAKE_SYSTEM_PROCESSOR MATCHES
        "^(arm64|ARM64|aarch64|AARCH64)$")
    if(SESUM_BUILD_ARM64_VARIANT)
        set(LIB_DIR "${LIB_DIR}${SESUM_BUILD_ARM64_VARIANT}")
    else()
        set(LIB_DIR "${LIB_DIR}arm64")
    endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES
        "^(x86_64|X86_64|amd64|AMD64)$")
    if(NOT SESUM_BUILD_X86_VARIANT)
        message(FATAL_ERROR "Internal error: no Sesum x86 build variant selected")
    endif()
    set(LIB_DIR "${LIB_DIR}${SESUM_BUILD_X86_VARIANT}")
else()
    message(FATAL_ERROR "Only arm64 and x86_64 supported")
endif ()

message(STATUS "Sesum native library package directory: ${LIB_DIR}")

if(WIN32)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static")
endif()

file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/sesum/${LIB_DIR}")

set(SR_LIB "sr")
add_library(${SR_LIB} SHARED cwrapper.h cwrapper.cpp)
if(SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE)
    # The same fat objects remain inside libsesum_impl.a for ordinary static
    # consumers.  Supplying them before that archive lets GCC optimize all
    # Sparse instantiations together; their archive copies are not extracted.
    target_sources(${SR_LIB} PRIVATE $<TARGET_OBJECTS:sesum_sparse_core_obj>)
    target_link_options(${SR_LIB} PRIVATE
        ${SESUM_SPARSE_DIRECT_FAT_LTO_FLAG}
        ${SESUM_SPARSE_LTO_PARTITION_FLAG})
    # GCC re-applies driver optimization flags while generating LTRANS
    # partitions.  Keep the Sparse-only compact policy last on the final link
    # so the global -funroll-loops setting cannot undo it during LTO.
    if(SESUM_COMPACT_SPARSE_CODE)
        target_link_options(${SR_LIB} PRIVATE -fno-unroll-loops)
    endif()
endif()
if(WIN32)
    set_target_properties(${SR_LIB} PROPERTIES PREFIX "lib")
endif()
target_include_directories(${SR_LIB} PUBLIC ./)
target_link_libraries(${SR_LIB} PUBLIC sesum_impl)

# Homebrew GCC archives inherit the macOS version of the machine on which the
# bottle was built.  Selecting them through the compiler unchanged would make
# a nominally portable Sesum dylib contain C++/OpenMP runtime objects for a
# newer OS.  Use architecture/compiler-matched static runtimes built for the
# deployment floors selected by the top-level project.  GCC's Darwin specs
# resolve these archives through -B; a plain -L does not override their
# absolute libstdc++/libgomp paths.
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(SESUM_MACOS_GNU_RUNTIME_DIR "" CACHE PATH
        "Override directory containing compatible libgomp.a and libstdc++.a")

    if(SESUM_MACOS_GNU_RUNTIME_DIR)
        set(SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR
            "${SESUM_MACOS_GNU_RUNTIME_DIR}")
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES
            "^(arm64|ARM64|aarch64|AARCH64)$"
            AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.2"
            AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.3")
        set(SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR
            "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtime/macos_arm64_gcc12")
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES
            "^(x86_64|X86_64|amd64|AMD64)$"
            AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "13.2"
            AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13.3")
        set(SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR
            "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtime/macos_x86_64_gcc13")
    else()
        message(FATAL_ERROR
            "No bundled macOS GNU runtime matches "
            "${CMAKE_SYSTEM_PROCESSOR} with GCC "
            "${CMAKE_CXX_COMPILER_VERSION}. Set "
            "-DSESUM_MACOS_GNU_RUNTIME_DIR=/absolute/runtime/directory")
    endif()

    if(NOT IS_ABSOLUTE "${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}")
        message(FATAL_ERROR
            "SESUM_MACOS_GNU_RUNTIME_DIR must be an absolute path.")
    endif()
    get_filename_component(SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR
        "${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}" REALPATH)

    foreach(SESUM_RUNTIME_NAME IN ITEMS libgomp.a libstdc++.a)
        set(SESUM_RUNTIME_ARCHIVE
            "${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}/${SESUM_RUNTIME_NAME}")
        if(NOT EXISTS "${SESUM_RUNTIME_ARCHIVE}")
            message(FATAL_ERROR
                "Missing bundled macOS GNU runtime: "
                "${SESUM_RUNTIME_ARCHIVE}")
        endif()
        file(SIZE "${SESUM_RUNTIME_ARCHIVE}" SESUM_RUNTIME_SIZE)
        if(SESUM_RUNTIME_SIZE EQUAL 0)
            message(FATAL_ERROR
                "Empty bundled macOS GNU runtime: ${SESUM_RUNTIME_ARCHIVE}")
        endif()

        execute_process(
            COMMAND "${CMAKE_CXX_COMPILER}"
                    "-B${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}/"
                    "-print-file-name=${SESUM_RUNTIME_NAME}"
            RESULT_VARIABLE SESUM_RUNTIME_LOOKUP_RESULT
            OUTPUT_VARIABLE SESUM_RUNTIME_LOOKUP
            OUTPUT_STRIP_TRAILING_WHITESPACE
            ERROR_VARIABLE SESUM_RUNTIME_LOOKUP_ERROR)
        get_filename_component(SESUM_RUNTIME_LOOKUP_REAL
            "${SESUM_RUNTIME_LOOKUP}" REALPATH)
        get_filename_component(SESUM_RUNTIME_ARCHIVE_REAL
            "${SESUM_RUNTIME_ARCHIVE}" REALPATH)
        if(NOT SESUM_RUNTIME_LOOKUP_RESULT EQUAL 0
                OR NOT "${SESUM_RUNTIME_LOOKUP_REAL}" STREQUAL
                       "${SESUM_RUNTIME_ARCHIVE_REAL}")
            message(FATAL_ERROR
                "GCC did not select bundled ${SESUM_RUNTIME_NAME} through "
                "-B; resolved '${SESUM_RUNTIME_LOOKUP}'. "
                "Driver error: ${SESUM_RUNTIME_LOOKUP_ERROR}")
        endif()
    endforeach()

    if("${CMAKE_OSX_DEPLOYMENT_TARGET}" STREQUAL "")
        message(FATAL_ERROR
            "The macOS GNU-runtime check requires a deployment target.")
    endif()

    set(SESUM_MACOS_RUNTIME_PROBE_SOURCE
        "${CMAKE_CURRENT_BINARY_DIR}/sesum_macos_runtime_probe.cpp")
    set(SESUM_MACOS_RUNTIME_PROBE_LIBRARY
        "${CMAKE_CURRENT_BINARY_DIR}/libsesum_macos_runtime_probe.dylib")
    file(WRITE "${SESUM_MACOS_RUNTIME_PROBE_SOURCE}" [=[
#include <omp.h>
#include <string>
#include <vector>
extern "C" __attribute__((visibility("default")))
int sesum_macos_runtime_probe() {
    std::vector<std::string> values(
        3, std::to_string(omp_get_max_threads()));
    int total = 0;
#pragma omp parallel reduction(+:total)
    total += static_cast<int>(values.at(0).size()) + omp_get_thread_num();
    return total;
}
]=])

    set(SESUM_MACOS_RUNTIME_PROBE_ARGS
        "-B${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}/"
        -nodefaultrpaths
        -std=c++17 -fPIC -fopenmp -static-libgcc -static-libstdc++
        "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}"
        -dynamiclib -Wl,-fatal_warnings -Wl,-undefined,error)
    if(NOT "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
        list(GET CMAKE_OSX_ARCHITECTURES 0 SESUM_MACOS_PROBE_ARCH)
        list(APPEND SESUM_MACOS_RUNTIME_PROBE_ARGS
            -arch "${SESUM_MACOS_PROBE_ARCH}")
    endif()
    if(NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "")
        set(SESUM_MACOS_PROBE_SYSROOT "${CMAKE_OSX_SYSROOT}")
        if(NOT IS_ABSOLUTE "${SESUM_MACOS_PROBE_SYSROOT}")
            execute_process(
                COMMAND /usr/bin/xcrun --sdk "${SESUM_MACOS_PROBE_SYSROOT}"
                        --show-sdk-path
                RESULT_VARIABLE SESUM_MACOS_SYSROOT_RESULT
                OUTPUT_VARIABLE SESUM_MACOS_PROBE_SYSROOT
                OUTPUT_STRIP_TRAILING_WHITESPACE)
            if(NOT SESUM_MACOS_SYSROOT_RESULT EQUAL 0)
                message(FATAL_ERROR
                    "Cannot resolve CMAKE_OSX_SYSROOT="
                    "'${CMAKE_OSX_SYSROOT}'.")
            endif()
        endif()
        list(APPEND SESUM_MACOS_RUNTIME_PROBE_ARGS
            -isysroot "${SESUM_MACOS_PROBE_SYSROOT}")
    endif()
    list(APPEND SESUM_MACOS_RUNTIME_PROBE_ARGS
        "${SESUM_MACOS_RUNTIME_PROBE_SOURCE}"
        -o "${SESUM_MACOS_RUNTIME_PROBE_LIBRARY}")

    execute_process(
        COMMAND "${CMAKE_CXX_COMPILER}" ${SESUM_MACOS_RUNTIME_PROBE_ARGS}
        RESULT_VARIABLE SESUM_MACOS_RUNTIME_PROBE_RESULT
        OUTPUT_VARIABLE SESUM_MACOS_RUNTIME_PROBE_OUTPUT
        ERROR_VARIABLE SESUM_MACOS_RUNTIME_PROBE_ERROR)
    if(NOT SESUM_MACOS_RUNTIME_PROBE_RESULT EQUAL 0)
        message(FATAL_ERROR
            "Bundled macOS GNU runtimes failed the C++/OpenMP dylib "
            "check.\n${SESUM_MACOS_RUNTIME_PROBE_OUTPUT}\n"
            "${SESUM_MACOS_RUNTIME_PROBE_ERROR}")
    endif()

    execute_process(
        COMMAND /usr/bin/xcrun vtool -show-build
                "${SESUM_MACOS_RUNTIME_PROBE_LIBRARY}"
        RESULT_VARIABLE SESUM_MACOS_VTOOL_RESULT
        OUTPUT_VARIABLE SESUM_MACOS_VTOOL_OUTPUT
        ERROR_VARIABLE SESUM_MACOS_VTOOL_ERROR)
    string(REGEX MATCH
        "minos[ \t]+([0-9]+\\.[0-9]+(\\.[0-9]+)?)"
        SESUM_MACOS_MINOS_MATCH "${SESUM_MACOS_VTOOL_OUTPUT}")
    if(NOT SESUM_MACOS_VTOOL_RESULT EQUAL 0
            OR "${CMAKE_MATCH_1}" STREQUAL ""
            OR NOT "${CMAKE_MATCH_1}" VERSION_EQUAL
                   "${CMAKE_OSX_DEPLOYMENT_TARGET}")
        message(FATAL_ERROR
            "GNU runtime check has wrong or missing minos; expected "
            "${CMAKE_OSX_DEPLOYMENT_TARGET}.\n"
            "${SESUM_MACOS_VTOOL_OUTPUT}\n${SESUM_MACOS_VTOOL_ERROR}")
    endif()

    execute_process(
        COMMAND /usr/bin/otool -L "${SESUM_MACOS_RUNTIME_PROBE_LIBRARY}"
        RESULT_VARIABLE SESUM_MACOS_OTOOL_RESULT
        OUTPUT_VARIABLE SESUM_MACOS_OTOOL_OUTPUT
        ERROR_VARIABLE SESUM_MACOS_OTOOL_ERROR)
    if(NOT SESUM_MACOS_OTOOL_RESULT EQUAL 0
            OR SESUM_MACOS_OTOOL_OUTPUT MATCHES
               "lib(gomp|stdc\\+\\+|gcc_s|atomic|quadmath)")
        message(FATAL_ERROR
            "GNU runtime check retained a dynamic compiler runtime.\n"
            "${SESUM_MACOS_OTOOL_OUTPUT}\n${SESUM_MACOS_OTOOL_ERROR}")
    endif()
    file(REMOVE
        "${SESUM_MACOS_RUNTIME_PROBE_SOURCE}"
        "${SESUM_MACOS_RUNTIME_PROBE_LIBRARY}")

    # This affects only the final sr driver invocation.  Suppress GCC's
    # absolute Homebrew runtime RPATHs: every GNU runtime selected above is
    # static, and retaining build-machine search paths would only leak host
    # details into the distributable dylib.  Compilation and the existing
    # Clang-prebuilt mimalloc archive remain completely untouched.
    target_link_options(${SR_LIB} PRIVATE
        "-B${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}/"
        -nodefaultrpaths)
    message(STATUS
        "Using bundled macOS GNU runtimes: "
        "${SESUM_SELECTED_MACOS_GNU_RUNTIME_DIR}")
endif()

# A wheel backend must not depend on the compiler's libgomp.so.1 (or, on
# ARM64, libatomic.so.1) being installed on the destination machine.  GCC has
# no -static-libgomp option, and the stock x86_64 libgomp.a shipped by many
# distributions is not PIC and therefore cannot be embedded in a shared
# library.  The repository therefore carries architecture-specific GCC-8 PIC
# archives for the portable Linux x86_64 and ARM64 builds.  They are built with
# --disable-shared --enable-static --disable-multilib --with-pic and with
# -ftls-model=global-dynamic so loading Sesum late into an already threaded
# Python process does not consume scarce static TLS.  Other GNU/Linux targets
# may supply an explicit PIC archive or use a compiler archive which passes the
# shared-link probe.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux"
        AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(SESUM_PIC_LIBGOMP_ARCHIVE "" CACHE FILEPATH
        "PIC-capable static libgomp archive embedded in the Linux backend")
    set(SESUM_PIC_LIBATOMIC_ARCHIVE "" CACHE FILEPATH
        "PIC-capable static libatomic archive embedded in the Linux backend")

    if(NOT SESUM_PIC_LIBGOMP_ARCHIVE
            AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "8"
            AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9")
        if(CMAKE_SYSTEM_PROCESSOR MATCHES
                "^(x86_64|X86_64|amd64|AMD64)$")
            set(SESUM_BUNDLED_PIC_LIBGOMP
                "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtime/linux_x86_64_gcc8/libgomp.a")
        elseif(CMAKE_SYSTEM_PROCESSOR MATCHES
                "^(arm64|ARM64|aarch64|AARCH64)$")
            set(SESUM_BUNDLED_PIC_LIBGOMP
                "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtime/linux_arm64_gcc8/libgomp.a")
        endif()
        if(DEFINED SESUM_BUNDLED_PIC_LIBGOMP
                AND EXISTS "${SESUM_BUNDLED_PIC_LIBGOMP}")
            set(SESUM_PIC_LIBGOMP_ARCHIVE
                "${SESUM_BUNDLED_PIC_LIBGOMP}")
        endif()
    endif()

    if(NOT SESUM_PIC_LIBGOMP_ARCHIVE)
        execute_process(
            COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libgomp.a
            OUTPUT_VARIABLE SESUM_PIC_LIBGOMP_ARCHIVE
            OUTPUT_STRIP_TRAILING_WHITESPACE)
    endif()
    if(NOT IS_ABSOLUTE "${SESUM_PIC_LIBGOMP_ARCHIVE}"
            OR NOT EXISTS "${SESUM_PIC_LIBGOMP_ARCHIVE}")
        message(FATAL_ERROR
            "A PIC-capable static libgomp.a is required for a portable Linux "
            "backend. Set -DSESUM_PIC_LIBGOMP_ARCHIVE=/absolute/path/libgomp.a")
    endif()

    if(NOT SESUM_PIC_LIBATOMIC_ARCHIVE)
        execute_process(
            COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libatomic.a
            OUTPUT_VARIABLE SESUM_PIC_LIBATOMIC_ARCHIVE
            OUTPUT_STRIP_TRAILING_WHITESPACE)
    endif()
    if(NOT IS_ABSOLUTE "${SESUM_PIC_LIBATOMIC_ARCHIVE}"
            OR NOT EXISTS "${SESUM_PIC_LIBATOMIC_ARCHIVE}")
        message(FATAL_ERROR
            "A static libatomic.a is required for a portable Linux backend. "
            "Set -DSESUM_PIC_LIBATOMIC_ARCHIVE=/absolute/path/libatomic.a")
    endif()

    # Fail during configure, before the expensive Sesum build, when a distro's
    # archive contains non-PIC TLS relocations.  This is common for x86_64
    # libgomp.a even though the compiler can use it for static executables.
    set(SESUM_OPENMP_PROBE_SOURCE
        "${CMAKE_CURRENT_BINARY_DIR}/sesum_static_openmp_probe.c")
    set(SESUM_OPENMP_PROBE_OBJECT
        "${CMAKE_CURRENT_BINARY_DIR}/sesum_static_openmp_probe.o")
    set(SESUM_OPENMP_PROBE_LIBRARY
        "${CMAKE_CURRENT_BINARY_DIR}/libsesum_static_openmp_probe.so")
    file(WRITE "${SESUM_OPENMP_PROBE_SOURCE}"
        "#include <omp.h>\nint sesum_openmp_probe(void) { return omp_get_max_threads(); }\n")
    execute_process(
        COMMAND ${CMAKE_C_COMPILER} -fPIC -fopenmp -c
                "${SESUM_OPENMP_PROBE_SOURCE}"
                -o "${SESUM_OPENMP_PROBE_OBJECT}"
        RESULT_VARIABLE SESUM_OPENMP_PROBE_COMPILE_RESULT
        ERROR_VARIABLE SESUM_OPENMP_PROBE_COMPILE_ERROR)
    if(SESUM_OPENMP_PROBE_COMPILE_RESULT EQUAL 0)
        execute_process(
            COMMAND ${CMAKE_CXX_COMPILER}
                    -shared -fno-openmp -static-libgcc -static-libstdc++
                    "${SESUM_OPENMP_PROBE_OBJECT}"
                    -Wl,--whole-archive
                    "${SESUM_PIC_LIBGOMP_ARCHIVE}"
                    "${SESUM_PIC_LIBATOMIC_ARCHIVE}"
                    -Wl,--no-whole-archive
                    -pthread -ldl -Wl,-z,defs
                    -o "${SESUM_OPENMP_PROBE_LIBRARY}"
            RESULT_VARIABLE SESUM_OPENMP_PROBE_LINK_RESULT
            ERROR_VARIABLE SESUM_OPENMP_PROBE_LINK_ERROR)
    else()
        set(SESUM_OPENMP_PROBE_LINK_RESULT 1)
    endif()
    if(NOT SESUM_OPENMP_PROBE_COMPILE_RESULT EQUAL 0
            OR NOT SESUM_OPENMP_PROBE_LINK_RESULT EQUAL 0)
        message(FATAL_ERROR
            "The selected static GNU runtime archives cannot be embedded in "
            "a Linux shared library. Use PIC-capable archives.\n"
            "Compile error: ${SESUM_OPENMP_PROBE_COMPILE_ERROR}\n"
            "Link error: ${SESUM_OPENMP_PROBE_LINK_ERROR}")
    endif()
    file(REMOVE
        "${SESUM_OPENMP_PROBE_SOURCE}"
        "${SESUM_OPENMP_PROBE_OBJECT}"
        "${SESUM_OPENMP_PROBE_LIBRARY}")

    message(STATUS
        "Embedding static GNU OpenMP runtime: ${SESUM_PIC_LIBGOMP_ARCHIVE}")
    message(STATUS
        "Embedding static GNU atomic runtime: ${SESUM_PIC_LIBATOMIC_ARCHIVE}")

    # -fopenmp remains active while compiling every target.  Disable only the
    # driver's automatic dynamic -lgomp on the final shared-library link, then
    # embed all runtime objects so archive ordering cannot leave GOMP symbols
    # unresolved.  --as-needed also discards a later transitive -latomic after
    # the static archive resolved it; --exclude-libs hides implementation
    # symbols from both runtimes.
    target_link_options(${SR_LIB} PRIVATE
        "-fno-openmp"
        "-pthread"
        "-Wl,--as-needed"
        "-Wl,-z,defs")
    target_link_libraries(${SR_LIB} PRIVATE
        "-Wl,--whole-archive"
        "${SESUM_PIC_LIBGOMP_ARCHIVE}"
        "${SESUM_PIC_LIBATOMIC_ARCHIVE}"
        "-Wl,--no-whole-archive"
        "${CMAKE_DL_LIBS}")
endif()

# Apple's -dead_strip is unsafe for the hand-written OpenBLAS x86 kernels:
# their fall-through blocks use .subsections_via_symbols and can be removed
# while still being reached from inside the public kernel symbol.  The link
# then succeeds but, for example, Haswell dgemm silently returns zeros.  ARM
# uses Accelerate rather than those kernels, so its existing size reduction is
# safe to retain.
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES
            "^(arm64|ARM64|aarch64|AARCH64)$")
        target_link_options(${SR_LIB} PRIVATE "-Wl,-dead_strip")
    elseif(UNIX AND NOT APPLE)
        target_link_options(${SR_LIB} PRIVATE
                "-Wl,--gc-sections"
                "-Wl,--exclude-libs,ALL"
                "-Wl,-s")
    endif()
endif()

# MinGW otherwise appends a large COFF symbol/string table to Release DLLs
# (about 17.5 MB in each previous x86 package) even though Python consumes only
# the explicit __declspec(dllexport) C ABI.  Garbage-collect unreferenced
# function/data sections and omit that non-runtime symbol table at link time;
# the PE export directory and executable code remain intact.
if(MINGW AND CMAKE_BUILD_TYPE MATCHES "^(Release|MinSizeRel)$")
    target_link_options(${SR_LIB} PRIVATE
        "-Wl,--gc-sections"
        "-Wl,-s")
endif()

# Only the small C ABI is consumed by Python.  Static libstdc++ and libgomp
# otherwise cause thousands of implementation symbols to be exported from the
# final macOS dylib.  Restricting exports preserves every public Sesum entry
# point while allowing the linker and strip tools to discard private metadata.
if(APPLE)
    set(SESUM_MACOS_EXPORTS
        "${CMAKE_CURRENT_SOURCE_DIR}/sesum_macos_exports.txt")
    set_property(TARGET ${SR_LIB} APPEND PROPERTY
        LINK_DEPENDS "${SESUM_MACOS_EXPORTS}")
    target_link_options(${SR_LIB} PRIVATE
        "-Wl,-exported_symbols_list,${SESUM_MACOS_EXPORTS}")

    # The export list above defines the complete supported C ABI.  Release
    # dylibs do not need the several-megabyte local C++ symbol table left by
    # statically embedded libstdc++/libgomp and the typed sparse kernels.
    # Apple's linker -x is equivalent to a safe `strip -x`: it removes local
    # symbol names only, never code, exports, relocations, or unwind data.
    if(CMAKE_BUILD_TYPE MATCHES "^(Release|MinSizeRel)$")
        target_link_options(${SR_LIB} PRIVATE "-Wl,-x")
    endif()
endif()

# GCC LTO can promote otherwise hidden implementation symbols into the ELF
# dynamic symbol table.  Python only consumes the documented C wrapper, so a
# version script keeps that ABI exact and lets --gc-sections discard code that
# was alive solely because of an accidental C++ export.
if(UNIX AND NOT APPLE)
    set(SESUM_LINUX_EXPORTS
        "${CMAKE_CURRENT_SOURCE_DIR}/sesum_linux_exports.map")
    set_property(TARGET ${SR_LIB} APPEND PROPERTY
        LINK_DEPENDS "${SESUM_LINUX_EXPORTS}")
    target_link_options(${SR_LIB} PRIVATE
        "-Wl,--version-script,${SESUM_LINUX_EXPORTS}")
endif()

if(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
        AND CMAKE_SYSTEM_PROCESSOR MATCHES
            "^(arm64|ARM64|aarch64|AARCH64)$")
    execute_process(
        COMMAND ${CMAKE_C_COMPILER} --print-resource-dir
        OUTPUT_VARIABLE CLANG_RESOURCE_DIR
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    if(CLANG_RESOURCE_DIR)
        find_library(CLANG_RT_BUILTINS_AARCH64
            NAMES clang_rt.builtins-aarch64 clang_rt.builtins-aarch64.lib
            PATHS "${CLANG_RESOURCE_DIR}/lib/windows"
            NO_DEFAULT_PATH
        )
        if(CLANG_RT_BUILTINS_AARCH64)
            target_link_libraries(${SR_LIB} PRIVATE "${CLANG_RT_BUILTINS_AARCH64}")
        endif()
    endif()
endif()

add_custom_command(TARGET ${SR_LIB} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
        $<TARGET_FILE:${SR_LIB}>
        ${CMAKE_SOURCE_DIR}/sesum/${LIB_DIR}
)
