ci: give the macOS OBS sub-build a version-carrying SDK path
Build / macOS (macos-latest) (push) Failing after 10s
Build / Linux (ubuntu-24.04) (push) Successful in 46s
Build / Windows (windows-latest) (push) Failing after 8m14s

Second macOS failure, after the Xcode-generator one: OBS's own
cmake/macos/compilerconfig.cmake reads the macOS SDK version by regex-matching
"MacOSX<major>.<minor>.sdk" out of CMAKE_OSX_SYSROOT, and hard-fails when that
does not match --

  string sub-command REGEX, mode MATCH needs at least 5 arguments
  Your macOS SDK version () is too low. The macOS 13.1 SDK (Xcode 14.2) is
  required to build OBS.

-- with an empty version in the message, which is the tell.

With upstream's Xcode generator CMAKE_OSX_SYSROOT stays the literal string
"macosx" and Xcode resolves it late, so that regex never runs against a real
path. With Ninja, which this project now uses because the CI runner has no
Xcode, CMake resolves it eagerly to `xcrun --show-sdk-path` -- and on a
Command-Line-Tools-only install that is the UNVERSIONED symlink
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk. So swapping the generator
moved the failure rather than removing it.

_resolve_versioned_macos_sdk now hands the sub-build a path whose filename
carries the version: a versioned sibling if the toolchain ships one (the
common layout), otherwise a symlink to the same SDK created under .deps/sdk
and named MacOSX<major>.<minor>.sdk. Either way clang gets the same SDK; only
the spelling of the path changes, which is all OBS's check looks at.

Also: the source's worker thread now backs off when the source is
unconfigured, instead of re-evaluating once a second forever, and resets the
backoff whenever the settings change -- a settings change is an operator
action and should retry immediately. Filling the settings in bumps the
generation counter and wakes the worker straight away, so the longer backoff
costs no responsiveness.

Linux re-verified: ctest 6/6.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
This commit is contained in:
2026-09-06 22:10:45 -07:00
co-authored by Claude Sonnet 5
parent 7146f78831
commit 0fbcb7c2f4
2 changed files with 85 additions and 1 deletions
+75
View File
@@ -68,6 +68,77 @@ function(_check_deps_version version)
return(PROPAGATE found CMAKE_PREFIX_PATH) return(PROPAGATE found CMAKE_PREFIX_PATH)
endfunction() endfunction()
# _resolve_versioned_macos_sdk: return an SDK path whose *filename* carries the
# version number, e.g. .../MacOSX15.5.sdk.
#
# Not upstream. OBS's own cmake/macos/compilerconfig.cmake reads the SDK
# version by regex-matching "MacOSX<major>.<minor>.sdk" out of
# CMAKE_OSX_SYSROOT, and hard-fails if that does not match:
#
# string sub-command REGEX, mode MATCH needs at least 5 arguments
# Your macOS SDK version () is too low.
#
# With upstream's Xcode generator CMAKE_OSX_SYSROOT stays the literal string
# "macosx" and Xcode resolves it late, so the regex never runs against a real
# path. With Ninja -- which this project uses because CI has no Xcode -- CMake
# resolves it eagerly to whatever `xcrun --show-sdk-path` returns, and on a
# Command-Line-Tools-only install that is the UNVERSIONED symlink
# /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk. Hence this: prefer a
# versioned sibling if the toolchain ships one, and otherwise synthesise a
# correctly-named symlink to the same SDK.
function(_resolve_versioned_macos_sdk out_path)
set(${out_path} "" PARENT_SCOPE)
execute_process(
COMMAND xcrun --show-sdk-path
OUTPUT_VARIABLE _sdk
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _rc
ERROR_QUIET
)
if(NOT _rc EQUAL 0 OR NOT _sdk)
return()
endif()
# Already versioned: nothing to do.
get_filename_component(_sdk_name "${_sdk}" NAME)
if(_sdk_name MATCHES "^MacOSX[0-9]+\\.[0-9]+\\.sdk$")
set(${out_path} "${_sdk}" PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND xcrun --show-sdk-version
OUTPUT_VARIABLE _sdk_version
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _rc
ERROR_QUIET
)
if(NOT _rc EQUAL 0 OR NOT _sdk_version MATCHES "^([0-9]+)\\.([0-9]+)")
return()
endif()
set(_short "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}")
# A versioned sibling next to the symlink is the common layout.
get_filename_component(_sdk_dir "${_sdk}" DIRECTORY)
if(EXISTS "${_sdk_dir}/MacOSX${_short}.sdk")
set(${out_path} "${_sdk_dir}/MacOSX${_short}.sdk" PARENT_SCOPE)
return()
endif()
# Otherwise make one, inside our own dependency directory.
set(_link_dir "${dependencies_dir}/sdk")
file(MAKE_DIRECTORY "${_link_dir}")
set(_link "${_link_dir}/MacOSX${_short}.sdk")
if(NOT EXISTS "${_link}")
file(CREATE_LINK "${_sdk}" "${_link}" SYMBOLIC)
endif()
if(EXISTS "${_link}")
message(STATUS "Using synthesised versioned macOS SDK path: ${_link}")
set(${out_path} "${_link}" PARENT_SCOPE)
endif()
endfunction()
# _setup_obs_studio: Create obs-studio build project, then build libobs and obs-frontend-api # _setup_obs_studio: Create obs-studio build project, then build libobs and obs-frontend-api
function(_setup_obs_studio) function(_setup_obs_studio)
if(NOT libobs_DIR) if(NOT libobs_DIR)
@@ -108,6 +179,10 @@ function(_setup_obs_studio)
if(CMAKE_OSX_DEPLOYMENT_TARGET) if(CMAKE_OSX_DEPLOYMENT_TARGET)
list(APPEND _cmake_extra "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") list(APPEND _cmake_extra "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif() endif()
_resolve_versioned_macos_sdk(_sdk_path)
if(_sdk_path)
list(APPEND _cmake_extra "-DCMAKE_OSX_SYSROOT=${_sdk_path}")
endif()
endif() endif()
message(STATUS "Configure ${label} (${arch})") message(STATUS "Configure ${label} (${arch})")
+10 -1
View File
@@ -237,6 +237,11 @@ void workerLoop(CameraSource *self)
} }
const bool config_changed = generation != connected_generation; const bool config_changed = generation != connected_generation;
if (config_changed) {
// A settings change is an operator action: try again immediately,
// whatever the previous attempt's backoff had grown to.
backoff_ms = kBackoffStartMs;
}
const bool needs_connect = const bool needs_connect =
!connected || config_changed || !connected || config_changed ||
(self->session && (self->session->state() == SessionState::Failed || (self->session && (self->session->state() == SessionState::Failed ||
@@ -253,7 +258,11 @@ void workerLoop(CameraSource *self)
if (!config.is_valid() || camera.empty()) { if (!config.is_valid() || camera.empty()) {
self->setStatus("not configured -- set the server URL, room, read key and camera"); self->setStatus("not configured -- set the server URL, room, read key and camera");
connected_generation = generation; connected_generation = generation;
backoff_ms = kBackoffStartMs; // Back off like any other unsuccessful attempt, so an
// unconfigured source is not a once-a-second no-op forever.
// Filling the settings in bumps the generation and wakes this
// thread immediately, so the backoff costs no responsiveness.
backoff_ms = backoff_ms * 2 < kBackoffMaxMs ? backoff_ms * 2 : kBackoffMaxMs;
} else { } else {
self->setStatus("connecting..."); self->setStatus("connecting...");
const TokenResult token = self->api->requestToken(config); const TokenResult token = self->api->requestToken(config);