feedBack-desktop/cmake/onnxruntime.cmake
2026-06-16 18:48:12 +02:00

203 lines
10 KiB
CMake

# ONNX Runtime acquisition for the ML note detector (Basic Pitch).
#
# Mirrors the NAM_AVAILABLE / RTNEURAL_AVAILABLE conditional pattern: if ONNX
# Runtime can be obtained, ONNXRUNTIME_AVAILABLE is set ON and the audio addon
# compiles with SLOPSMITH_ONNX_SUPPORT=1; otherwise the build still succeeds and
# the engine falls back to the YIN PitchDetector / ChordScorer (Constitution VII).
#
# Delivery: a pinned, prebuilt CPU release fetched at configure time with a
# pinned URL + SHA-256 (Constitution V — reproducible builds). For fully offline
# / pre-seeded builds, set -DSLOPSMITH_ONNXRUNTIME_ROOT=/path/to/extracted/onnxruntime
# (the directory containing include/ and lib/) and the fetch is skipped.
#
# Exports (cache/parent scope):
# ONNXRUNTIME_AVAILABLE ON/OFF
# ONNXRUNTIME_INCLUDE_DIR header directory
# ONNXRUNTIME_IMPORT_LIB library to link against
# ONNXRUNTIME_RUNTIME_LIB shared lib that must sit next to slopsmith_audio.node
# Plain variable, not a cache entry: the version is pinned in lock-step with
# the per-asset SHA-256 table below, so it must always be the value in this
# file. A CACHE STRING would keep a stale version from a reused build dir
# (a branch on a different version), making the hash check fail and silently
# disable ONNX.
set(ONNXRUNTIME_VERSION "1.20.1")
# --- Resolve the prebuilt asset for this OS/arch --------------------------
set(_ort_base "https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}")
set(_ort_ok ON)
# Only x64 / arm64 prebuilt assets exist. An unrecognised processor must
# disable ONNX (clean YIN fallback) — never default to an x64 asset, which
# would only fail later at link/load on e.g. Windows ARM64 or 32-bit ARM.
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64|x64")
set(_ort_asset "onnxruntime-win-x64-${ONNXRUNTIME_VERSION}")
set(_ort_ext "zip")
set(_ort_sha "78d447051e48bd2e1e778bba378bec4ece11191c9e538cf7b2c4a4565e8f5581")
set(_ort_import "onnxruntime.lib")
set(_ort_runtime "onnxruntime.dll")
set(_ort_providers "onnxruntime_providers_shared.dll")
else()
set(_ort_ok OFF)
message(STATUS "ONNX Runtime: unsupported Windows arch '${CMAKE_SYSTEM_PROCESSOR}' — ML note detection disabled")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(_ort_ext "tgz")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
set(_ort_asset "onnxruntime-osx-arm64-${ONNXRUNTIME_VERSION}")
set(_ort_sha "b678fc3c2354c771fea4fba420edeccfba205140088334df801e7fc40e83a57a")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|x64")
set(_ort_asset "onnxruntime-osx-x86_64-${ONNXRUNTIME_VERSION}")
set(_ort_sha "0f73006813af2a1a5d1723ed7dfb694fc629d15037124081bb61b7bf7d99fc78")
else()
set(_ort_ok OFF)
message(STATUS "ONNX Runtime: unsupported macOS arch '${CMAKE_SYSTEM_PROCESSOR}' — ML note detection disabled")
endif()
set(_ort_import "libonnxruntime.dylib")
set(_ort_runtime "libonnxruntime.${ONNXRUNTIME_VERSION}.dylib")
set(_ort_providers "libonnxruntime_providers_shared.dylib")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(_ort_ext "tgz")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
set(_ort_asset "onnxruntime-linux-aarch64-${ONNXRUNTIME_VERSION}")
set(_ort_sha "ae4fedbdc8c18d688c01306b4b50c63de3445cdf2dbd720e01a2fa3810b8106a")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|x64")
set(_ort_asset "onnxruntime-linux-x64-${ONNXRUNTIME_VERSION}")
set(_ort_sha "67db4dc1561f1e3fd42e619575c82c601ef89849afc7ea85a003abbac1a1a105")
else()
set(_ort_ok OFF)
message(STATUS "ONNX Runtime: unsupported Linux arch '${CMAKE_SYSTEM_PROCESSOR}' — ML note detection disabled")
endif()
set(_ort_import "libonnxruntime.so")
# SONAME is libonnxruntime.so.1 — that exact name must sit next to the addon.
set(_ort_runtime "libonnxruntime.so.1")
set(_ort_providers "libonnxruntime_providers_shared.so")
else()
set(_ort_ok OFF)
message(STATUS "ONNX Runtime: unsupported platform '${CMAKE_SYSTEM_NAME}' — ML note detection disabled")
endif()
# --- Obtain the runtime: explicit root override, or pinned fetch ----------
set(_ort_root "")
if(_ort_ok AND DEFINED SLOPSMITH_ONNXRUNTIME_ROOT)
if(EXISTS "${SLOPSMITH_ONNXRUNTIME_ROOT}/include/onnxruntime_cxx_api.h")
set(_ort_root "${SLOPSMITH_ONNXRUNTIME_ROOT}")
message(STATUS "ONNX Runtime: using prepopulated root ${_ort_root}")
else()
message(WARNING "SLOPSMITH_ONNXRUNTIME_ROOT='${SLOPSMITH_ONNXRUNTIME_ROOT}' "
"has no include/onnxruntime_cxx_api.h — ignoring")
endif()
endif()
# Download + extract the prebuilt archive. Every failure mode here is SOFT —
# offline machine, download error, hash mismatch, bad layout all just disable
# ML detection (YIN fallback). Configure must never abort on this, so we use
# file(DOWNLOAD) with a status check rather than FetchContent, which raises a
# FATAL_ERROR when the archive can't be fetched.
if(_ort_ok AND _ort_root STREQUAL "")
set(_ort_url "${_ort_base}/${_ort_asset}.${_ort_ext}")
set(_ort_archive "${CMAKE_BINARY_DIR}/_deps/${_ort_asset}.${_ort_ext}")
set(_ort_extract "${CMAKE_BINARY_DIR}/_deps/onnxruntime")
set(_ort_header "${_ort_extract}/${_ort_asset}/include/onnxruntime_cxx_api.h")
# On a clean build tree _deps does not exist yet — create it so the
# file(DOWNLOAD) below can open its destination file instead of failing
# and silently disabling ONNX support on every fresh configure.
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/_deps")
# Download — cached: a prior configure's archive with a matching hash is
# reused, so reconfigure / clean-tree rebuilds don't re-fetch.
set(_ort_have OFF)
if(EXISTS "${_ort_archive}")
file(SHA256 "${_ort_archive}" _ort_got)
if(_ort_got STREQUAL "${_ort_sha}")
set(_ort_have ON)
endif()
endif()
if(NOT _ort_have)
message(STATUS "ONNX Runtime: downloading ${_ort_asset}.${_ort_ext}")
# Bounded timeouts so a network stall can't hang cmake configure
# indefinitely — the status check below then soft-falls to YIN.
file(DOWNLOAD "${_ort_url}" "${_ort_archive}" STATUS _ort_dlst
TLS_VERIFY ON INACTIVITY_TIMEOUT 30 TIMEOUT 600)
list(GET _ort_dlst 0 _ort_dlcode)
if(_ort_dlcode EQUAL 0 AND EXISTS "${_ort_archive}")
file(SHA256 "${_ort_archive}" _ort_got)
if(_ort_got STREQUAL "${_ort_sha}")
set(_ort_have ON)
else()
message(WARNING "ONNX Runtime: SHA-256 mismatch on ${_ort_asset} "
"— ML note detection disabled (YIN fallback)")
endif()
else()
list(GET _ort_dlst 1 _ort_dlmsg)
message(STATUS "ONNX Runtime: download failed (${_ort_dlmsg}) "
"— ML note detection disabled (YIN fallback)")
endif()
endif()
# Extract — only when the archive is good and not already unpacked.
if(_ort_have AND NOT EXISTS "${_ort_header}")
file(REMOVE_RECURSE "${_ort_extract}")
file(MAKE_DIRECTORY "${_ort_extract}")
file(ARCHIVE_EXTRACT INPUT "${_ort_archive}" DESTINATION "${_ort_extract}")
endif()
if(_ort_have AND EXISTS "${_ort_header}")
set(_ort_root "${_ort_extract}/${_ort_asset}")
message(STATUS "ONNX Runtime ${ONNXRUNTIME_VERSION}: ready (${_ort_asset})")
elseif(_ort_have)
message(WARNING "ONNX Runtime: unexpected archive layout "
"— ML note detection disabled (YIN fallback)")
endif()
endif()
# --- Publish results -------------------------------------------------------
# Verify the link + runtime libraries actually exist before declaring ONNX
# available. A prepopulated SLOPSMITH_ONNXRUNTIME_ROOT (or an unexpected
# archive layout) could carry the headers but miss/rename the libs — without
# this check configure would pass and the build would only fail later at link
# or the post-build copy, defeating the intended soft-fall to YIN.
set(_ort_import_path "${_ort_root}/lib/${_ort_import}")
set(_ort_runtime_path "${_ort_root}/lib/${_ort_runtime}")
# The shared-provider stub library ONNX Runtime dlopen()s alongside the main
# runtime. The CPU EP works without it, but staging it avoids a session-
# creation failure on builds/versions where it is consulted — see below.
set(_ort_providers_path "${_ort_root}/lib/${_ort_providers}")
if(_ort_ok AND NOT _ort_root STREQUAL ""
AND NOT (EXISTS "${_ort_import_path}" AND EXISTS "${_ort_runtime_path}"))
message(WARNING "ONNX Runtime: headers found but lib '${_ort_import}' / "
"'${_ort_runtime}' missing under ${_ort_root}/lib "
"— ML note detection disabled (YIN fallback)")
set(_ort_root "")
endif()
if(_ort_ok AND NOT _ort_root STREQUAL "")
# FORCE so a reconfigure always reflects the current probe result rather
# than a stale entry from an earlier configure.
set(ONNXRUNTIME_AVAILABLE ON CACHE INTERNAL "" FORCE)
set(ONNXRUNTIME_INCLUDE_DIR "${_ort_root}/include" CACHE INTERNAL "" FORCE)
set(ONNXRUNTIME_IMPORT_LIB "${_ort_import_path}" CACHE INTERNAL "" FORCE)
set(ONNXRUNTIME_RUNTIME_LIB "${_ort_runtime_path}" CACHE INTERNAL "" FORCE)
# Optional — present in the standard releases but staged only if it exists,
# so an unexpected layout never fails the build.
if(EXISTS "${_ort_providers_path}")
set(ONNXRUNTIME_PROVIDERS_LIB "${_ort_providers_path}" CACHE INTERNAL "" FORCE)
else()
unset(ONNXRUNTIME_PROVIDERS_LIB CACHE)
endif()
message(STATUS "ONNX Runtime available — ML note detection enabled")
else()
set(ONNXRUNTIME_AVAILABLE OFF CACHE INTERNAL "" FORCE)
# Clear any stale paths a previous successful configure cached, so a
# later reconfigure that loses ONNX can't leave downstream steps
# pointed at a runtime that is no longer there.
unset(ONNXRUNTIME_INCLUDE_DIR CACHE)
unset(ONNXRUNTIME_IMPORT_LIB CACHE)
unset(ONNXRUNTIME_RUNTIME_LIB CACHE)
unset(ONNXRUNTIME_PROVIDERS_LIB CACHE)
message(STATUS "ONNX Runtime not available — ML note detection disabled (YIN fallback)")
endif()