# Blosc - Blocked Shuffling and Compression Library
#
# Copyright (c) 2021  Blosc Development Team <blosc@blosc.org>
# https://blosc.org
# License: BSD 3-Clause (see LICENSE.txt)
#
# See LICENSE.txt for details about copyright and rights to use.

# CMake build system for Blosc
# ============================
#
# Available options:
#
#   BUILD_STATIC: default ON
#       build the static version of the Blosc library
#   BUILD_SHARED: default ON
#       build the shared library version of the Blosc library
#   BUILD_TESTS: default ON
#       build test programs and generates the "test" target
#   BUILD_FUZZERS: default ON
#       build fuzz test programs and generates the "fuzz" target
#   BUILD_BENCHMARKS: default ON
#       build the benchmark programs
#   BUILD_EXAMPLES: default ON
#       build the examples programs
#   BUILD_PLUGINS: default ON
#       build the plugins programs
#   BUILD_LITE: default OFF
#       build a lite version (only with BloscLZ and LZ4/LZ4HC) of the blosc library
#   DEACTIVATE_AVX2: default OFF
#       do not attempt to build with AVX2 instructions
#   DEACTIVATE_AVX512: default OFF
#       do not attempt to build with AVX512 instructions
#   DEACTIVATE_ZLIB: default OFF
#       do not include support for the Zlib library
#   DEACTIVATE_ZSTD: default OFF
#       do not include support for the Zstd library
#   WITH_ZLIB_OPTIM: default ON
#       set WITH_OPTIM when building the fetched zlib-ng library; setting OFF is useful for wasm32 targets
#   PREFER_EXTERNAL_LZ4: default OFF
#       when found, use the installed LZ4 libs instead of fetching upstream sources
#   PREFER_EXTERNAL_ZLIB: default OFF
#       when found, use the installed Zlib/Zlib-ng libs instead of fetching upstream sources
#   PREFER_EXTERNAL_ZSTD: default OFF
#       when found, use the installed Zstd libs instead of fetching upstream sources
#   TEST_INCLUDE_BENCH_SHUFFLE_1: default ON
#       add a test that runs the benchmark program passing "shuffle" with 1
#       thread as second parameter
#   TEST_INCLUDE_BENCH_SHUFFLE_N: default ON
#       add a test that runs the benchmark program passing "shuffle" with all
#       threads as second parameter
#   TEST_INCLUDE_BENCH_BITSHUFFLE_1: default ON
#       add a test that runs the benchmark program passing "bitshuffle" with 1
#       thread as second parameter
#   TEST_INCLUDE_BENCH_BITSHUFFLE_N: default ON
#       add a test that runs the benchmark program passing "bitshuffle" with
#       all threads as second parameter
#   TEST_INCLUDE_BENCH_SUITE: default OFF
#       add a test that runs the benchmark program passing "suite"
#       as first parameter
#   TEST_INCLUDE_BENCH_HARDSUITE: default OFF
#       add a test that runs the benchmark program passing "hardsuite"
#       as first parameter
#   TEST_INCLUDE_BENCH_EXTREMESUITE: default OFF
#       add a test that runs the benchmark program passing "extremesuite"
#       as first parameter
#   TEST_INCLUDE_BENCH_DEBUGSUITE: default OFF
#       add a test that runs the benchmark program passing "debugsuite"
#       as first parameter
#
# Components:
#
#    LIB: includes blosc2.so
#    DEV: static includes blosc2.a and blosc2.h

if(NOT WIN32)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
endif()

cmake_minimum_required(VERSION 3.16.3)
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_GREATER 3.4)
  cmake_policy(SET CMP0063 NEW)
endif()
# The project can have ASM (zstd 1.5.2 is starting to use it)
project(blosc LANGUAGES C ASM)

# parse the full version numbers from blosc2.h
file(READ include/blosc2.h _blosc2_h_contents)
string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_MAJOR[ \t]+([0-9]+).*"
    "\\1" BLOSC2_VERSION_MAJOR ${_blosc2_h_contents})
string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_MINOR[ \t]+([0-9]+).*"
    "\\1" BLOSC2_VERSION_MINOR ${_blosc2_h_contents})
string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_RELEASE[ \t]+([0-9]+).*"
    "\\1" BLOSC2_VERSION_PATCH ${_blosc2_h_contents})
string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_STRING[ \t]+\"([-0-9A-Za-z.]+)\".*"
    "\\1" BLOSC2_VERSION_STRING ${_blosc2_h_contents})

message("Configuring for Blosc version: " ${BLOSC2_VERSION_STRING})

# options
option(BUILD_STATIC
    "Build a static version of the blosc library." ON)
option(BUILD_SHARED
    "Build a shared library version of the blosc library." ON)
option(BUILD_TESTS
    "Build test programs from the blosc compression library" ON)
option(BUILD_FUZZERS
    "Build fuzzer programs from the blosc compression library" ${BUILD_STATIC})
option(BUILD_BENCHMARKS
    "Build benchmark programs from the blosc compression library" ON)
option(BUILD_EXAMPLES
    "Build example programs from the blosc compression library" ON)
option(BUILD_PLUGINS
    "Build plugins programs from the blosc compression library" ON)
option(BUILD_LITE
    "Build a lite version (only with BloscLZ and LZ4/LZ4HC) of the blosc library." OFF)
option(DEACTIVATE_AVX2
    "Do not attempt to build with AVX2 instructions" OFF)
option(DEACTIVATE_AVX512
    "Do not attempt to build with AVX512 instructions" OFF)
option(DEACTIVATE_ZLIB
    "Do not include support for the Zlib library." OFF)
option(DEACTIVATE_ZSTD
    "Do not include support for the Zstd library." OFF)
option(PREFER_EXTERNAL_LZ4
    "Find and use external LZ4 library instead of fetching sources." OFF)
option(PREFER_EXTERNAL_ZLIB
    "Find and use external ZLIB library instead of fetching sources." OFF)
option(PREFER_EXTERNAL_ZSTD
    "Find and use external ZSTD library instead of fetching sources." OFF)
option(WITH_ZLIB_OPTIM
    "Set WITH_OPTIM for ZLIB, turning off is useful for compiling for wasm32 targets." ON)

set(BLOSC_DEPENDENCY_MODE "BUNDLED" CACHE STRING
    "Dependency resolution mode: BUNDLED uses fetched dependencies, EXTERNAL requires system dependencies, AUTO tries system dependencies before fetching.")
set_property(CACHE BLOSC_DEPENDENCY_MODE PROPERTY STRINGS BUNDLED EXTERNAL AUTO)
set(BLOSC_ENABLE_ZFP "AUTO" CACHE STRING
    "ZFP codec support: AUTO enables it when available, ON requires it, OFF disables it.")
set_property(CACHE BLOSC_ENABLE_ZFP PROPERTY STRINGS AUTO ON OFF)

set(BLOSC_LZ4_VERSION "1.10.0" CACHE STRING
    "lz4 version to use when fetching internal sources.")
set(BLOSC_LZ4_SOURCE_DIR "" CACHE PATH
    "Optional local lz4 checkout to use instead of downloading sources.")
set(BLOSC_ZLIBNG_VERSION "2.3.3" CACHE STRING
    "zlib-ng version to use when fetching internal sources.")
set(BLOSC_ZLIBNG_SOURCE_DIR "" CACHE PATH
    "Optional local zlib-ng checkout to use instead of downloading sources.")
set(BLOSC_ZSTD_VERSION "1.5.7" CACHE STRING
    "zstd version to use when fetching internal sources.")
set(BLOSC_ZSTD_SOURCE_DIR "" CACHE PATH
    "Optional local zstd checkout to use instead of downloading sources.")
set(BLOSC_ZFP_VERSION "1.0.1" CACHE STRING
    "zfp version to use when fetching plugin sources.")
set(BLOSC_ZFP_SOURCE_DIR "" CACHE PATH
    "Optional local zfp checkout to use instead of downloading plugin sources.")

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(GNUInstallDirs)

if(MINGW)
    # This gets us DLL resource information when compiling on MinGW.
    if(NOT CMAKE_RC_COMPILER)
        set(CMAKE_RC_COMPILER windres.exe)
    endif()
endif()

if(ENABLE_ASAN)
    message(STATUS "Enabling ASAN")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Os -fno-omit-frame-pointer -fsanitize=address")
endif()

if(BUILD_PLUGINS AND BUILD_LITE)
    message(FATAL_ERROR "Both plugins and lite Blosc versions can not be built at the same time.")
endif()

if(BUILD_LITE)
    set(DEACTIVATE_ZLIB ON)
    set(DEACTIVATE_ZSTD ON)
endif()

string(TOUPPER "${BLOSC_DEPENDENCY_MODE}" BLOSC_DEPENDENCY_MODE)
set(BLOSC_DEPENDENCY_MODE "${BLOSC_DEPENDENCY_MODE}" CACHE STRING
    "Dependency resolution mode: BUNDLED uses fetched dependencies, EXTERNAL requires system dependencies, AUTO tries system dependencies before fetching." FORCE)
if(NOT BLOSC_DEPENDENCY_MODE STREQUAL "BUNDLED" AND
   NOT BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL" AND
   NOT BLOSC_DEPENDENCY_MODE STREQUAL "AUTO")
    message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE must be one of BUNDLED, EXTERNAL, or AUTO")
endif()

string(TOUPPER "${BLOSC_ENABLE_ZFP}" BLOSC_ENABLE_ZFP)
set(BLOSC_ENABLE_ZFP "${BLOSC_ENABLE_ZFP}" CACHE STRING
    "ZFP codec support: AUTO enables it when available, ON requires it, OFF disables it." FORCE)
if(NOT BLOSC_ENABLE_ZFP STREQUAL "AUTO" AND
   NOT BLOSC_ENABLE_ZFP STREQUAL "ON" AND
   NOT BLOSC_ENABLE_ZFP STREQUAL "OFF")
    message(FATAL_ERROR "BLOSC_ENABLE_ZFP must be one of AUTO, ON, or OFF")
endif()

# Threads
set(CMAKE_THREAD_PREFER_PTHREAD TRUE) # pre 3.1
set(THREADS_PREFER_PTHREAD_FLAG TRUE) # CMake 3.1+
if(WIN32)
    # try to use the system library
    find_package(Threads)
else()
    find_package(Threads REQUIRED)
endif()
if(Threads_FOUND)
    set(HAVE_THREADS ON)
else()
    set(HAVE_THREADS OFF)
endif()

set(BLOSC_FIND_EXTERNAL_LZ4 FALSE)
set(BLOSC_REQUIRE_EXTERNAL_LZ4 FALSE)
if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL")
    set(BLOSC_FIND_EXTERNAL_LZ4 TRUE)
    set(BLOSC_REQUIRE_EXTERNAL_LZ4 TRUE)
elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_LZ4)
    set(BLOSC_FIND_EXTERNAL_LZ4 TRUE)
endif()

if(BLOSC_FIND_EXTERNAL_LZ4)
    find_package(LZ4 CONFIG NAMES lz4)
    if(LZ4_FOUND)
        set(HAVE_LZ4_CONFIG TRUE)
    else()
        find_package(LZ4 MODULE)
    endif()
endif()

if(NOT LZ4_FOUND AND BLOSC_REQUIRE_EXTERNAL_LZ4)
    message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external LZ4 package. Install LZ4 or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.")
endif()

if(NOT LZ4_FOUND)
    include(FetchContent)
    set(LZ4_BUILD_CLI OFF CACHE BOOL "" FORCE)
    set(LZ4_BUILD_LEGACY_LZ4C OFF CACHE BOOL "" FORCE)
    set(LZ4_BUNDLED_MODE ON CACHE BOOL "" FORCE)
    set(LZ4_POSITION_INDEPENDENT_LIB ON CACHE BOOL "" FORCE)

    if(BLOSC_LZ4_SOURCE_DIR)
        message(STATUS "Using lz4 via FetchContent from local checkout: ${BLOSC_LZ4_SOURCE_DIR}")
        FetchContent_Declare(
            lz4
            SOURCE_DIR "${BLOSC_LZ4_SOURCE_DIR}"
            SOURCE_SUBDIR build/cmake)
    else()
        message(STATUS "Using lz4 ${BLOSC_LZ4_VERSION} via FetchContent download")
        FetchContent_Declare(
            lz4
            URL "https://github.com/lz4/lz4/archive/refs/tags/v${BLOSC_LZ4_VERSION}.tar.gz"
            DOWNLOAD_EXTRACT_TIMESTAMP TRUE
            SOURCE_SUBDIR build/cmake)
    endif()
    FetchContent_GetProperties(lz4)
    if(NOT lz4_POPULATED)
        if(POLICY CMP0169)
            cmake_policy(PUSH)
            cmake_policy(SET CMP0169 OLD)
        endif()
        FetchContent_Populate(lz4)
        if(POLICY CMP0169)
            cmake_policy(POP)
        endif()
        add_subdirectory("${lz4_SOURCE_DIR}/build/cmake" "${lz4_BINARY_DIR}" EXCLUDE_FROM_ALL)
    endif()

    set(LZ4_FETCHED TRUE)
    set(LZ4_FOUND TRUE)
    set(LZ4_SOURCE_DIR "${lz4_SOURCE_DIR}")
    set(LZ4_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/lz4")
    if(TARGET lz4_static)
        set_target_properties(lz4_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
        set_property(TARGET lz4_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES
            "$<BUILD_INTERFACE:${LZ4_SOURCE_DIR}/lib>"
            "$<INSTALL_INTERFACE:${LZ4_INSTALL_INCLUDEDIR}>")
    endif()
endif()

if(NOT DEACTIVATE_ZLIB)
    set(BLOSC_FIND_EXTERNAL_ZLIB FALSE)
    set(BLOSC_REQUIRE_EXTERNAL_ZLIB FALSE)
    if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL")
        set(BLOSC_FIND_EXTERNAL_ZLIB TRUE)
        set(BLOSC_REQUIRE_EXTERNAL_ZLIB TRUE)
    elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_ZLIB)
        set(BLOSC_FIND_EXTERNAL_ZLIB TRUE)
    endif()

    if(BLOSC_FIND_EXTERNAL_ZLIB)
        find_package(ZLIB_NG CONFIG NAMES zlib-ng)
        if(ZLIB_NG_FOUND)
            set(HAVE_ZLIB_NG TRUE)
            set(HAVE_ZLIB_NG_CONFIG TRUE)
        else()
            find_package(ZLIB_NG MODULE)
            if(ZLIB_NG_FOUND)
                set(HAVE_ZLIB_NG TRUE)
            else()
                find_package(ZLIB)
            endif()
        endif()
    endif()

    if(NOT (ZLIB_NG_FOUND OR ZLIB_FOUND) AND BLOSC_REQUIRE_EXTERNAL_ZLIB)
        message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external zlib-ng or zlib package. Install one or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.")
    endif()

    if(NOT (ZLIB_NG_FOUND OR ZLIB_FOUND))
        include(FetchContent)
        if (NOT WITH_ZLIB_OPTIM)
            set(WITH_OPTIM OFF CACHE BOOL "Disable zlib-ng architecture optimizations when fetched via FetchContent" FORCE)
            set(WITH_RUNTIME_CPU_DETECTION OFF CACHE BOOL "Disable zlib-ng runtime CPU detection when fetched via FetchContent" FORCE)
        endif()
        set(SKIP_INSTALL_ALL ON CACHE BOOL "Skip zlib-ng install targets when fetched via FetchContent" FORCE)
        set(BUILD_TESTING OFF CACHE BOOL "Disable zlib-ng tests when fetched via FetchContent" FORCE)
        set(WITH_GTEST OFF CACHE BOOL "Disable zlib-ng GTest when fetched via FetchContent" FORCE)
        set(WITH_FUZZERS OFF CACHE BOOL "Disable zlib-ng fuzzers when fetched via FetchContent" FORCE)
        set(WITH_BENCHMARKS OFF CACHE BOOL "Disable zlib-ng benchmarks when fetched via FetchContent" FORCE)
        set(WITH_BENCHMARK_APPS OFF CACHE BOOL "Disable zlib-ng benchmark apps when fetched via FetchContent" FORCE)

        if(BLOSC_ZLIBNG_SOURCE_DIR)
            message(STATUS "Using zlib-ng via FetchContent from local checkout: ${BLOSC_ZLIBNG_SOURCE_DIR}")
            FetchContent_Declare(
                zlib_ng
                SOURCE_DIR "${BLOSC_ZLIBNG_SOURCE_DIR}")
        else()
            message(STATUS "Using zlib-ng ${BLOSC_ZLIBNG_VERSION} via FetchContent download")
            FetchContent_Declare(
                zlib_ng
                URL "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/${BLOSC_ZLIBNG_VERSION}.tar.gz"
                DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
        endif()
        FetchContent_MakeAvailable(zlib_ng)

        set(HAVE_ZLIB_NG TRUE)
        set(ZLIB_NG_FETCHED TRUE)
        set(ZLIB_NG_FOUND TRUE)
        set(ZLIB_NG_BUILD_DIR "${zlib_ng_BINARY_DIR}")
        set(ZLIB_NG_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/zlib-ng")
        set(ZLIB_NG_INSTALL_TARGET "")
        if(TARGET zlib-ng-static)
            get_target_property(ZLIB_NG_INSTALL_TARGET zlib-ng-static ALIASED_TARGET)
            if(NOT ZLIB_NG_INSTALL_TARGET)
                set(ZLIB_NG_INSTALL_TARGET zlib-ng-static)
            endif()
        elseif(TARGET zlib-ng)
            get_target_property(ZLIB_NG_INSTALL_TARGET zlib-ng ALIASED_TARGET)
            if(NOT ZLIB_NG_INSTALL_TARGET)
                set(ZLIB_NG_INSTALL_TARGET zlib-ng)
            endif()
        endif()
        if(ZLIB_NG_INSTALL_TARGET)
            set_target_properties(${ZLIB_NG_INSTALL_TARGET} PROPERTIES POSITION_INDEPENDENT_CODE ON)
            set_property(TARGET ${ZLIB_NG_INSTALL_TARGET} PROPERTY INTERFACE_INCLUDE_DIRECTORIES
                "$<BUILD_INTERFACE:${zlib_ng_SOURCE_DIR}>"
                "$<BUILD_INTERFACE:${ZLIB_NG_BUILD_DIR}>"
                "$<INSTALL_INTERFACE:${ZLIB_NG_INSTALL_INCLUDEDIR}>")
        endif()
    endif()
    set(HAVE_ZLIB TRUE)
endif()


if(NOT DEACTIVATE_ZSTD)
    set(BLOSC_FIND_EXTERNAL_ZSTD FALSE)
    set(BLOSC_REQUIRE_EXTERNAL_ZSTD FALSE)
    if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL")
        set(BLOSC_FIND_EXTERNAL_ZSTD TRUE)
        set(BLOSC_REQUIRE_EXTERNAL_ZSTD TRUE)
    elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_ZSTD)
        set(BLOSC_FIND_EXTERNAL_ZSTD TRUE)
    endif()

    if(BLOSC_FIND_EXTERNAL_ZSTD)
        find_package(ZSTD CONFIG NAMES zstd)
        if(ZSTD_FOUND)
            set(HAVE_ZSTD_CONFIG TRUE)
        else()
            find_package(ZSTD MODULE)
        endif()
    endif()

    if(NOT ZSTD_FOUND AND BLOSC_REQUIRE_EXTERNAL_ZSTD)
        message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external Zstd package. Install Zstd or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.")
    endif()

    if(NOT ZSTD_FOUND)
        include(FetchContent)
        set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "Disable zstd programs when fetched via FetchContent" FORCE)
        set(ZSTD_BUILD_SHARED OFF CACHE BOOL "Disable shared zstd library when fetched via FetchContent" FORCE)
        set(ZSTD_BUILD_STATIC ON CACHE BOOL "Enable static zstd library when fetched via FetchContent" FORCE)
        set(ZSTD_BUILD_TESTS OFF CACHE BOOL "Disable zstd tests when fetched via FetchContent" FORCE)
        set(ZSTD_BUILD_CONTRIB OFF CACHE BOOL "Disable zstd contrib targets when fetched via FetchContent" FORCE)
        set(ZSTD_MULTITHREAD_SUPPORT OFF CACHE BOOL "Disable zstd multithread support when fetched via FetchContent" FORCE)

        if(BLOSC_ZSTD_SOURCE_DIR)
            message(STATUS "Using zstd via FetchContent from local checkout: ${BLOSC_ZSTD_SOURCE_DIR}")
            FetchContent_Declare(
                zstd
                SOURCE_DIR "${BLOSC_ZSTD_SOURCE_DIR}"
                SOURCE_SUBDIR build/cmake)
        else()
            message(STATUS "Using zstd ${BLOSC_ZSTD_VERSION} via FetchContent download")
            FetchContent_Declare(
                zstd
                URL "https://github.com/facebook/zstd/archive/refs/tags/v${BLOSC_ZSTD_VERSION}.tar.gz"
                DOWNLOAD_EXTRACT_TIMESTAMP TRUE
                SOURCE_SUBDIR build/cmake)
        endif()
        FetchContent_GetProperties(zstd)
        if(NOT zstd_POPULATED)
            if(POLICY CMP0169)
                cmake_policy(PUSH)
                cmake_policy(SET CMP0169 OLD)
            endif()
            FetchContent_Populate(zstd)
            if(POLICY CMP0169)
                cmake_policy(POP)
            endif()
            add_subdirectory("${zstd_SOURCE_DIR}/build/cmake" "${zstd_BINARY_DIR}" EXCLUDE_FROM_ALL)
        endif()

        set(HAVE_ZSTD TRUE)
        set(ZSTD_FETCHED TRUE)
        set(ZSTD_FOUND TRUE)
        set(ZSTD_BUILD_DIR "${zstd_BINARY_DIR}")
        set(ZSTD_SOURCE_DIR "${zstd_SOURCE_DIR}")
        set(ZSTD_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/zstd")
        if(TARGET libzstd_static)
            set_target_properties(libzstd_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
            set_property(TARGET libzstd_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES
                "$<BUILD_INTERFACE:${ZSTD_SOURCE_DIR}/lib>"
                "$<INSTALL_INTERFACE:${ZSTD_INSTALL_INCLUDEDIR}>")
            # Silence GCC false positives in fetched legacy zstd sources on some Linux/amd64 builds.
            target_compile_options(libzstd_static PRIVATE
                $<$<COMPILE_LANG_AND_ID:C,GNU>:-Wno-maybe-uninitialized>)
        endif()
    else()
        set(HAVE_ZSTD TRUE)
    endif()
endif()

set(HAVE_ZFP FALSE)
set(BLOSC_ZFP_PROVIDER "NONE")
set(BLOSC_ZFP_TARGET "")
set(ZFP_FOUND FALSE)
if(BUILD_PLUGINS AND NOT BLOSC_ENABLE_ZFP STREQUAL "OFF")
    set(BLOSC_FIND_EXTERNAL_ZFP FALSE)
    set(BLOSC_ALLOW_BUNDLED_ZFP FALSE)
    if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL")
        set(BLOSC_FIND_EXTERNAL_ZFP TRUE)
    elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO")
        set(BLOSC_FIND_EXTERNAL_ZFP TRUE)
        set(BLOSC_ALLOW_BUNDLED_ZFP TRUE)
    else()
        set(BLOSC_ALLOW_BUNDLED_ZFP TRUE)
    endif()

    if(BLOSC_ENABLE_ZFP STREQUAL "ON" AND NOT BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL")
        set(BLOSC_ALLOW_BUNDLED_ZFP TRUE)
    endif()

    if(BLOSC_FIND_EXTERNAL_ZFP)
        find_package(zfp CONFIG QUIET)
        if(NOT zfp_FOUND)
            find_package(ZFP CONFIG QUIET)
        endif()
        if(NOT zfp_FOUND AND NOT ZFP_FOUND)
            find_package(ZFP MODULE QUIET)
        endif()
        if(zfp_FOUND OR ZFP_FOUND)
            set(ZFP_FOUND TRUE)
            set(BLOSC_ZFP_PROVIDER "EXTERNAL")
            foreach(BLOSC_ZFP_CANDIDATE zfp ZFP::zfp zfp::zfp ZFP::ZFP)
                if(TARGET ${BLOSC_ZFP_CANDIDATE} AND NOT BLOSC_ZFP_TARGET)
                    set(BLOSC_ZFP_TARGET ${BLOSC_ZFP_CANDIDATE})
                endif()
            endforeach()
            if(BLOSC_ZFP_TARGET AND NOT TARGET zfp)
                add_library(zfp INTERFACE IMPORTED)
                target_link_libraries(zfp INTERFACE ${BLOSC_ZFP_TARGET})
            endif()
        endif()
    endif()

    if(NOT ZFP_FOUND AND BLOSC_ALLOW_BUNDLED_ZFP)
        set(BLOSC_ZFP_PROVIDER "BUNDLED")
    endif()

    if(BLOSC_ZFP_PROVIDER STREQUAL "NONE")
        if(BLOSC_ENABLE_ZFP STREQUAL "ON")
            message(FATAL_ERROR "BLOSC_ENABLE_ZFP=ON requires ZFP support, but no external ZFP package was found and bundled ZFP is disabled by BLOSC_DEPENDENCY_MODE=EXTERNAL.")
        else()
            message(STATUS "ZFP codec support disabled: no external ZFP package found in BLOSC_DEPENDENCY_MODE=EXTERNAL")
        endif()
    else()
        set(HAVE_ZFP TRUE)
        message(STATUS "ZFP codec support enabled (${BLOSC_ZFP_PROVIDER})")
    endif()
endif()

if(BUILD_PLUGINS)
    set(HAVE_PLUGINS TRUE)
endif()

# create the config.h file
configure_file("${PROJECT_SOURCE_DIR}/blosc/config.h.in"
               "${PROJECT_BINARY_DIR}/blosc/config.h")

# now make sure that you set the build directory on your "Include" path when compiling
include_directories("${PROJECT_BINARY_DIR}/blosc/")

# If the build type is not set, default to Release.
set(BLOSC_DEFAULT_BUILD_TYPE Release)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "No build type specified. Defaulting to '${BLOSC_DEFAULT_BUILD_TYPE}'.")
    set(CMAKE_BUILD_TYPE ${BLOSC_DEFAULT_BUILD_TYPE} CACHE STRING
            "Choose the type of build." FORCE)

    # Set the possible values of build type for cmake-gui
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
            "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

# Propagate CMAKE_OSX_ARCHITECTURES env variable into CMAKE_SYSTEM_PROCESSOR
if(DEFINED ENV{CMAKE_OSX_ARCHITECTURES})
    if("$ENV{CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
        set(CMAKE_SYSTEM_PROCESSOR arm64)
    endif()
endif()

# Based on the target system's processor and the compiler being used,
# set build variables indicating which hardware features can be targeted
# by the compiler. Note we DO NOT check which hardware features are supported
# by this (the host) system, because we want to be able to support compiling
# for newer hardware on older machines as well as cross-compilation.
message(STATUS "Building for system processor ${CMAKE_SYSTEM_PROCESSOR}")
message(STATUS "Building for compiler ID ${CMAKE_C_COMPILER_ID}")
if(CMAKE_SYSTEM_PROCESSOR MATCHES i386|i686|x86_64|amd64|AMD64)
    if(CMAKE_C_COMPILER_ID MATCHES GNU)
        # We need C99 (GNU99 more exactly)
        SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
        set(COMPILER_SUPPORT_SSE2 TRUE)
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.7)
            set(COMPILER_SUPPORT_AVX2 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX2 FALSE)
        endif()
        # GCC 10.3.2 (the version in manylinux_2014) seems to have issues supporting dynamic dispatching
        # of AVX512.  GCC 11.4 is the first minimal version that works well here.
        # That means that Linux wheels will have AVX512 disabled, but that's life.
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.4)
            set(COMPILER_SUPPORT_AVX512 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX512 FALSE)
        endif()
    elseif(CMAKE_C_COMPILER_ID MATCHES Clang|AppleClang)
        set(COMPILER_SUPPORT_SSE2 TRUE)
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.2)
            set(COMPILER_SUPPORT_AVX2 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX2 FALSE)
        endif()
        # Clang 13 is the minimum version that we know that works with AVX512 dynamic dispatch.
        # Perhaps lesser versions work too, better to err on the safe side.
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0)
            set(COMPILER_SUPPORT_AVX512 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX512 FALSE)
        endif()
    elseif(CMAKE_C_COMPILER_ID MATCHES Intel|IntelLLVM)
        # All Intel compilers since the introduction of AVX512 in 2016 should support it, so activate all SIMD flavors
        set(COMPILER_SUPPORT_SSE2 TRUE)
        set(COMPILER_SUPPORT_AVX2 TRUE)
        set(COMPILER_SUPPORT_AVX512 TRUE)
    elseif(MSVC)
        set(COMPILER_SUPPORT_SSE2 TRUE)
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 18.00.30501)
            set(COMPILER_SUPPORT_AVX2 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX2 FALSE)
        endif()
        # AVX512 starts to be supported since Visual Studio 17 15.0
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.10.25017)
            set(COMPILER_SUPPORT_AVX512 TRUE)
        else()
            set(COMPILER_SUPPORT_AVX512 FALSE)
        endif()
    else()
        set(COMPILER_SUPPORT_SSE2 FALSE)
        set(COMPILER_SUPPORT_AVX2 FALSE)
        set(COMPILER_SUPPORT_AVX512 FALSE)
        # Unrecognized compiler. Emit a warning message to let the user know hardware-acceleration won't be available.
        message(WARNING "Unable to determine which ${CMAKE_SYSTEM_PROCESSOR} hardware features are supported by the C compiler (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}).")
    endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES armv7l|aarch64|arm64)
    if(CMAKE_C_COMPILER_ID MATCHES GNU)
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2)
            set(COMPILER_SUPPORT_NEON TRUE)
        else()
            set(COMPILER_SUPPORT_NEON FALSE)
        endif()
    elseif(CMAKE_C_COMPILER_ID MATCHES Clang|AppleClang)
        if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.3)
            set(COMPILER_SUPPORT_NEON TRUE)
        else()
            set(COMPILER_SUPPORT_NEON FALSE)
        endif()
    else()
        set(COMPILER_SUPPORT_NEON FALSE)
        # Unrecognized compiler. Emit a warning message to let the user know hardware-acceleration won't be available.
        message(WARNING "Unable to determine which ${CMAKE_SYSTEM_PROCESSOR} hardware features are supported by the C compiler (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}).")
    endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
    if(CMAKE_C_COMPILER_ID MATCHES GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 8)
        set(COMPILER_SUPPORT_ALTIVEC TRUE)
    elseif(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 13)
        set(COMPILER_SUPPORT_ALTIVEC TRUE)
    else()
        set(COMPILER_SUPPORT_ALTIVEC FALSE)
    endif()
else()
    # If the target system processor isn't recognized, emit a warning message to alert the user
    # that hardware-acceleration support won't be available but allow configuration to proceed.
    message(WARNING "Unrecognized system processor ${CMAKE_SYSTEM_PROCESSOR}. Cannot determine which hardware features (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}) supports, so hardware-accelerated implementations will not be available.")
endif()

# disable AVX2 if specified
if(DEACTIVATE_AVX2)
    set(COMPILER_SUPPORT_AVX2 FALSE)
    # AVX512 functions in bitshuffle depend on AVX2 too
    set(COMPILER_SUPPORT_AVX512 FALSE)
endif()

# disable AVX512 if specified
if(DEACTIVATE_AVX512)
    set(COMPILER_SUPPORT_AVX512 FALSE)
endif()

# flags
# @TODO: set -Wall
# @NOTE: -O3 is enabled in Release mode (CMAKE_BUILD_TYPE="Release")

# Set the "-msse2" build flag only if the CMAKE_C_FLAGS is not already set.
# Probably "-msse2" should be appended to CMAKE_C_FLAGS_RELEASE.
if(CMAKE_C_COMPILER_ID MATCHES GNU|Clang|Intel|IntelLLVM)
    if(NOT CMAKE_C_FLAGS AND COMPILER_SUPPORT_SSE2)
        set(CMAKE_C_FLAGS -msse2 CACHE STRING "C flags." FORCE)
    endif()
endif()

if(CMAKE_C_COMPILER_ID MATCHES Intel|IntelLLVM|Clang OR HAIKU)
    # We need to tell Intel and Clang compilers about what level of POSIX they support
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=600")
endif()

if(MSVC)
    if(NOT CMAKE_C_FLAGS)
        set(CMAKE_C_FLAGS "/Ox" CACHE STRING "C flags." FORCE)
    endif()

    # Turn off misguided "secure CRT" warnings in MSVC.
    # Microsoft wants people to use the MS-specific <function>_s
    # versions of certain C functions but this is difficult to do
    # in platform-independent code.
    add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
    add_compile_options(-Wall -Wextra)
endif()

if(WIN32)
    # For some supporting headers
    include_directories("${CMAKE_CURRENT_SOURCE_DIR}/blosc")
endif()

if(NOT DEFINED BLOSC_IS_SUBPROJECT)
    if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
        set(BLOSC_IS_SUBPROJECT FALSE)
    else()
        set(BLOSC_IS_SUBPROJECT TRUE)
        message(STATUS "Detected that BLOSC is used a subproject.")
    endif()
endif()

if(NOT DEFINED BLOSC_INSTALL)
    if(BLOSC_IS_SUBPROJECT)
        set(BLOSC_INSTALL FALSE)
    else()
        set(BLOSC_INSTALL TRUE)
    endif()
endif()

# include directories
include_directories(include)

# subdirectories
set(SOURCES)

if(BUILD_PLUGINS)
    enable_testing()
    add_subdirectory(plugins)
    if(DEFINED ZFP_SOURCE_DIR)
        include_directories(${ZFP_SOURCE_DIR}/include)
    endif()
endif()

# Determine which linkage the unified target will use
if(BUILD_SHARED AND NOT BUILD_STATIC)
    if(NOT BUILD_SHARED_LIBS)
        message(WARNING "BUILD_SHARED_LIBS is OFF, but BUILD_SHARED is ON and BUILD_STATIC is OFF, which takes precedence, so Blosc2::blosc2 is a shared library")
    endif()
    set(UNIFIED_TARGET_ALIAS blosc2_shared)
elseif(NOT BUILD_SHARED AND BUILD_STATIC)
    if(BUILD_SHARED_LIBS)
        message(WARNING "BUILD_SHARED_LIBS is ON, but BUILD_SHARED is OFF and BUILD_STATIC is ON, which takes precedence, so Blosc2::blosc2 is a static library")
    endif()
    set(UNIFIED_TARGET_ALIAS blosc2_static)
else()
    # If both BUILD_SHARED and BUILD_STATIC are set, which is the default,
    # fallback to using BUILD_SHARED_LIBS to determine whether to set
    # Blosc2::blosc2 to static or shared.
    if(BUILD_SHARED_LIBS)
        set(UNIFIED_TARGET_ALIAS blosc2_shared)
    else()
        set(UNIFIED_TARGET_ALIAS blosc2_static)
    endif()
endif()

add_subdirectory(blosc)

if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
    add_subdirectory(compat)
endif()

if(BUILD_FUZZERS)
    if(NOT BUILD_STATIC)
        message(FATAL_ERROR "BUILD_FUZZERS requires BUILD_STATIC to be enabled.")
    endif()
    enable_testing()
    add_subdirectory(tests/fuzz)
endif()

if(BUILD_BENCHMARKS)
    add_subdirectory(bench)
endif()

if(BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()

# collecting SOURCES is now complete
if(BUILD_SHARED)
    target_sources(blosc2_shared PRIVATE ${SOURCES})
endif()
if(BUILD_STATIC)
    target_sources(blosc2_static PRIVATE ${SOURCES})
endif()
if(BUILD_TESTS)
    target_sources(blosc_testing PRIVATE ${SOURCES})
endif()

# install targets
if(BLOSC_INSTALL)
    include(GNUInstallDirs)

    # C++ files
    install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV)
    install(FILES ${PROJECT_SOURCE_DIR}/include/b2nd.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV)
    install(FILES
            ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-export.h
            ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-common.h
            ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-stdio.h
            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blosc2 COMPONENT DEV)
    if(BUILD_PLUGINS)
        install(FILES
                ${PROJECT_SOURCE_DIR}/include/blosc2/filters-registry.h
                ${PROJECT_SOURCE_DIR}/include/blosc2/codecs-registry.h
                ${PROJECT_SOURCE_DIR}/include/blosc2/tuners-registry.h
                DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blosc2 COMPONENT DEV)
    endif()

    if(BUILD_SHARED)
        install(TARGETS blosc2_shared
                LIBRARY COMPONENT LIB
                ARCHIVE COMPONENT DEV
                RUNTIME COMPONENT LIB)
    endif()
    if(BUILD_STATIC)
        install(TARGETS blosc2_static COMPONENT DEV)
    endif()
    # Bundled dependency headers are private implementation details and are not
    # installed.  Bundled dependency objects are folded into libblosc2.a for
    # static builds and into libblosc2 shared libraries by the regular linker.

    # config files
    include(CMakePackageConfigHelpers)

    # we need a general location for Unix and Windows to install our
    # Blosc2Config.cmake files to. This is defined in CMake:
    # https://cmake.org/cmake/help/latest/command/find_package.html#config-mode-search-procedure
    if(NOT Blosc2_INSTALL_CMAKEDIR)
        if(WIN32 AND NOT MINGW)
            set(Blosc2_INSTALL_CMAKEDIR "cmake")
        else()
            set(Blosc2_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Blosc2")
        endif()
    endif()

    # CMake config file
    # This stores our targets and find and populates the targets we depend on,
    # including third party interface libraries that we added.
    set(Blosc2_INSTALL_TARGET_NAMES)
    set(Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES)
    if(BUILD_SHARED)
        list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2_shared)
    endif()
    if(BUILD_STATIC)
        list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2_static)
    endif()
    # Add unified target
    add_library(blosc2 INTERFACE)
    list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2)
    target_link_libraries(blosc2 INTERFACE ${UNIFIED_TARGET_ALIAS})
    configure_file(
        ${PROJECT_SOURCE_DIR}/Blosc2Config.cmake.in
        ${CMAKE_CURRENT_BINARY_DIR}/Blosc2Config.cmake
        @ONLY
    )
    install(TARGETS ${Blosc2_INSTALL_TARGET_NAMES}
        EXPORT Blosc2Targets
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    )
    if(Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES)
        install(TARGETS ${Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES}
            EXPORT Blosc2Targets
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/blosc2
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/blosc2
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        )
    endif()
    install(EXPORT Blosc2Targets
        FILE Blosc2Targets.cmake
        NAMESPACE Blosc2::
        DESTINATION ${Blosc2_INSTALL_CMAKEDIR}
    )
    write_basic_package_version_file("Blosc2ConfigVersion.cmake"
        VERSION ${BLOSC2_VERSION_STRING}
        COMPATIBILITY SameMajorVersion
    )
    install(
        FILES
            ${CMAKE_CURRENT_BINARY_DIR}/Blosc2Config.cmake
            ${CMAKE_CURRENT_BINARY_DIR}/Blosc2ConfigVersion.cmake
        DESTINATION ${Blosc2_INSTALL_CMAKEDIR}
    )

    # CMake Find*.cmake files used in Blosc2Config.cmake
    install(
        DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/
        DESTINATION ${Blosc2_INSTALL_CMAKEDIR}/Modules
    )

    # pkg-config .pc file
    configure_file(
         "${CMAKE_CURRENT_SOURCE_DIR}/blosc2.pc.in"
         "${CMAKE_CURRENT_BINARY_DIR}/blosc2.pc"
         @ONLY)
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/blosc2.pc"
            DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT DEV)

    # uninstaller
    if(NOT TARGET uninstall)
        configure_file(
            "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
            "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
            @ONLY)
        add_custom_target(uninstall
            COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
    endif()
endif()


# packaging
if(NOT BLOSC_IS_SUBPROJECT)
    include(InstallRequiredSystemLibraries)

    set(CPACK_GENERATOR TGZ ZIP)
    set(CPACK_SOURCE_GENERATOR TGZ ZIP)
    set(CPACK_PACKAGE_VERSION_MAJOR ${BLOSC2_VERSION_MAJOR})
    set(CPACK_PACKAGE_VERSION_MINOR ${BLOSC2_VERSION_MINOR})
    set(CPACK_PACKAGE_VERSION_PATCH ${BLOSC2_VERSION_PATCH})
    set(CPACK_PACKAGE_VERSION ${BLOSC_STRING_VERSION})
    set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.rst")
    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
            "A blocking, shuffling and lossless compression library")
    set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
    set(CPACK_SOURCE_IGNORE_FILES "/build.*;.*~;\\\\.git.*;\\\\.DS_Store")
    set(CPACK_STRIP_FILES TRUE)
    set(CPACK_SOURCE_STRIP_FILES TRUE)

    include(CPack)
endif()
