Files
obs-streamer-tools-plugin/cmake/LiveKitSDK.cmake
T
shadowdaoandClaude Sonnet 5 e595173049 Link the pinned LiveKit C++ SDK into the core library
Adds cmake/LiveKitSDK.cmake, adapted from
livekit-examples/cpp-example-collection's helper of the same name, with the
VERSION="latest" GitHub-API resolution path removed: this project pins an
exact client-sdk-cpp release (1.10.1, the newest tag as of today), and the
pin should not be silently bypassable. The module also now exports the
runtime shared libraries so packaging can stage liblivekit/liblivekit_ffi
next to the plugin module later.

core/ links LiveKit::livekit PUBLIC. A new smoke test proves the SDK is not
just linked but loadable and callable: livekit::initialize()/shutdown()
round-trip in-process, a second initialize() reports "already initialized",
the log level round-trips, and the SDK's generated LIVEKIT_BUILD_VERSION is
asserted equal to the version CMake pinned (so a stale extracted SDK
directory fails loudly rather than being silently reused).

Also adds core/tests/test_util.h, a dependency-free assertion harness that
keeps running after a failure and prints a pass/fail count, so CI output says
how much actually ran instead of dying on the first bare assert().

cmake_minimum_required goes 3.16 -> 3.19 (file(ARCHIVE_EXTRACT)).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-06 21:22:14 -07:00

210 lines
8.3 KiB
CMake

# LiveKitSDK.cmake
#
# Downloads the prebuilt LiveKit C++ SDK (livekit/client-sdk-cpp) release
# asset for the host OS/arch, extracts it, and points
# `find_package(LiveKit CONFIG REQUIRED)` at it.
#
# Adapted from livekit-examples/cpp-example-collection's cmake/LiveKitSDK.cmake
# (fetched 2026-09-06). Deliberate changes from the upstream reference:
#
# 1. VERSION must be an exact release number. The upstream helper accepted
# VERSION "latest" and resolved it through the GitHub releases API at
# configure time. That is exactly what this project must not do: the
# design doc calls for a pinned release tag, treating version bumps as
# deliberate work (the SDK is young and ships roughly weekly). Dropping
# the "latest" path also removes a GitHub-API call -- and its rate
# limiting / GITHUB_TOKEN plumbing -- from every CI configure.
# 2. Exports LIVEKIT_SDK_RUNTIME_LIBS: the shared libraries that must be
# staged next to the built OBS plugin module for it to load at runtime
# (liblivekit + liblivekit_ffi). Upstream examples run out of the build
# tree and never needed this; a redistributable OBS plugin does.
# 3. Exports LIVEKIT_SDK_INCLUDE_DIR / _LIB_DIR / _BIN_DIR for packaging.
#
# Usage:
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# include(LiveKitSDK)
# livekit_sdk_setup(VERSION "1.10.1" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk")
# find_package(LiveKit CONFIG REQUIRED)
include_guard(GLOBAL)
# -------------------- Host detection --------------------
function(_lk_detect_host out_os out_arch)
if(WIN32)
set(_os "windows")
elseif(APPLE)
set(_os "macos")
elseif(UNIX)
set(_os "linux")
else()
message(FATAL_ERROR "LiveKitSDK: unsupported host OS")
endif()
# Prefer the *target* processor when cross-compiling is expressed through
# CMAKE_OSX_ARCHITECTURES (macOS CI runners are arm64 but may target x64).
set(_proc "${CMAKE_HOST_SYSTEM_PROCESSOR}")
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
list(LENGTH CMAKE_OSX_ARCHITECTURES _n_arch)
if(_n_arch GREATER 1)
message(FATAL_ERROR
"LiveKitSDK: CMAKE_OSX_ARCHITECTURES lists ${_n_arch} architectures "
"(${CMAKE_OSX_ARCHITECTURES}). client-sdk-cpp ships single-arch "
"dylibs only, so universal binaries are not supported. Build one "
"architecture at a time.")
endif()
list(GET CMAKE_OSX_ARCHITECTURES 0 _proc)
endif()
string(TOLOWER "${_proc}" _proc_l)
if(_proc_l MATCHES "^(x86_64|amd64)$")
set(_arch "x64")
elseif(_proc_l MATCHES "^(arm64|aarch64)$")
set(_arch "arm64")
else()
message(FATAL_ERROR "LiveKitSDK: unsupported host arch: ${_proc}")
endif()
set(${out_os} "${_os}" PARENT_SCOPE)
set(${out_arch} "${_arch}" PARENT_SCOPE)
endfunction()
function(_lk_default_triple out_triple)
_lk_detect_host(_os _arch)
set(${out_triple} "${_os}-${_arch}" PARENT_SCOPE)
endfunction()
function(_lk_archive_ext out_ext)
if(WIN32)
set(${out_ext} "zip" PARENT_SCOPE)
else()
set(${out_ext} "tar.gz" PARENT_SCOPE)
endif()
endfunction()
# -------------------- Public entrypoint --------------------
# livekit_sdk_setup(
# VERSION <x.y.z> REQUIRED, exact -- "latest" is rejected
# SDK_DIR <dir> REQUIRED, where the archive is extracted
# [REPO <org/repo>] default: livekit/client-sdk-cpp
# [SHA256 <hash>] optional: verify the downloaded archive
# [TRIPLE <os-arch>] optional override (e.g. ubuntu-24.04-x64)
# [DOWNLOAD_DIR <dir>] default: <build>/_downloads
# [NO_DOWNLOAD] fail instead of downloading if absent
# )
function(livekit_sdk_setup)
set(options NO_DOWNLOAD)
set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR)
cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "" ${ARGN})
if(NOT LK_VERSION)
message(FATAL_ERROR "livekit_sdk_setup: VERSION is required")
endif()
if(LK_VERSION STREQUAL "latest")
message(FATAL_ERROR
"livekit_sdk_setup: VERSION=\"latest\" is deliberately not supported. "
"This project pins an exact client-sdk-cpp release; see the comment at "
"the top of cmake/LiveKitSDK.cmake.")
endif()
if(NOT LK_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "livekit_sdk_setup: VERSION must be x.y.z, got '${LK_VERSION}'")
endif()
if(NOT LK_SDK_DIR)
message(FATAL_ERROR "livekit_sdk_setup: SDK_DIR is required")
endif()
if(NOT LK_REPO)
set(LK_REPO "livekit/client-sdk-cpp")
endif()
if(NOT LK_DOWNLOAD_DIR)
set(LK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_downloads")
endif()
if(NOT LK_TRIPLE)
_lk_default_triple(LK_TRIPLE)
endif()
_lk_archive_ext(_ext)
set(_archive "livekit-sdk-${LK_TRIPLE}-${LK_VERSION}.${_ext}")
set(_url "https://github.com/${LK_REPO}/releases/download/v${LK_VERSION}/${_archive}")
set(_archive_path "${LK_DOWNLOAD_DIR}/${_archive}")
# The archive contains a single top-level folder named after the asset.
set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${LK_VERSION}")
file(MAKE_DIRECTORY "${LK_DOWNLOAD_DIR}")
file(MAKE_DIRECTORY "${LK_SDK_DIR}")
if(NOT EXISTS "${_extracted_root}/lib/cmake")
if(LK_NO_DOWNLOAD)
message(FATAL_ERROR
"LiveKitSDK: SDK not found at:\n ${_extracted_root}\nand NO_DOWNLOAD was set.")
endif()
message(STATUS "LiveKitSDK: downloading ${_url}")
if(LK_SHA256)
file(DOWNLOAD "${_url}" "${_archive_path}"
SHOW_PROGRESS TLS_VERIFY ON
EXPECTED_HASH "SHA256=${LK_SHA256}"
STATUS _st LOG _log)
else()
file(DOWNLOAD "${_url}" "${_archive_path}"
SHOW_PROGRESS TLS_VERIFY ON
STATUS _st LOG _log)
endif()
list(GET _st 0 _code)
list(GET _st 1 _msg)
if(NOT _code EQUAL 0)
file(REMOVE "${_archive_path}")
message(STATUS "LiveKitSDK: download log:\n${_log}")
message(FATAL_ERROR
"LiveKitSDK: download failed\nURL: ${_url}\nStatus: ${_code}\nMessage: ${_msg}\n"
"If this triple has no release asset, pass TRIPLE explicitly.")
endif()
# Remove any previous partial extraction.
file(REMOVE_RECURSE "${_extracted_root}")
message(STATUS "LiveKitSDK: extracting ${_archive_path}")
file(ARCHIVE_EXTRACT INPUT "${_archive_path}" DESTINATION "${LK_SDK_DIR}")
endif()
if(NOT EXISTS "${_extracted_root}/lib/cmake/LiveKit/LiveKitConfig.cmake")
message(FATAL_ERROR
"LiveKitSDK: extracted SDK does not look valid (missing "
"lib/cmake/LiveKit/LiveKitConfig.cmake)\nExpected under: ${_extracted_root}")
endif()
# Make find_package(LiveKit CONFIG REQUIRED) work in the caller's scope.
list(PREPEND CMAKE_PREFIX_PATH "${_extracted_root}")
set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" PARENT_SCOPE)
set(LiveKit_DIR "${_extracted_root}/lib/cmake/LiveKit" PARENT_SCOPE)
# --- Runtime libraries, for staging next to the plugin module ------------
# Windows keeps the DLLs in bin/ and the import libs in lib/; the Unix
# platforms put the shared objects directly in lib/.
if(WIN32)
file(GLOB _runtime_libs "${_extracted_root}/bin/*.dll")
elseif(APPLE)
file(GLOB _runtime_libs "${_extracted_root}/lib/*.dylib")
else()
file(GLOB _runtime_libs "${_extracted_root}/lib/*.so" "${_extracted_root}/lib/*.so.*")
endif()
if(NOT _runtime_libs)
message(FATAL_ERROR
"LiveKitSDK: found no runtime shared libraries under ${_extracted_root}. "
"The release layout may have changed for version ${LK_VERSION}.")
endif()
set(LIVEKIT_SDK_EXTRACTED_ROOT "${_extracted_root}" CACHE PATH "LiveKit SDK extracted root" FORCE)
set(LIVEKIT_SDK_INCLUDE_DIR "${_extracted_root}/include" CACHE PATH "LiveKit SDK include dir" FORCE)
set(LIVEKIT_SDK_LIB_DIR "${_extracted_root}/lib" CACHE PATH "LiveKit SDK lib dir" FORCE)
set(LIVEKIT_SDK_BIN_DIR "${_extracted_root}/bin" CACHE PATH "LiveKit SDK bin dir" FORCE)
set(LIVEKIT_SDK_RUNTIME_LIBS "${_runtime_libs}" CACHE STRING "LiveKit SDK runtime shared libraries" FORCE)
set(LIVEKIT_SDK_URL_USED "${_url}" CACHE STRING "LiveKit SDK URL used" FORCE)
set(LIVEKIT_SDK_VERSION_RESOLVED "${LK_VERSION}" CACHE STRING "LiveKit SDK version" FORCE)
set(LIVEKIT_SDK_TRIPLE_USED "${LK_TRIPLE}" CACHE STRING "LiveKit SDK triple" FORCE)
message(STATUS "LiveKitSDK: using SDK ${LK_VERSION} (${LK_TRIPLE}) at ${_extracted_root}")
endfunction()