diff --git a/CMakeLists.txt b/CMakeLists.txt index 8754bd8..15bfea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,8 +184,119 @@ if(OS_WINDOWS AND NOT libobs_DIR) endif() endif() +# ...and the exact same problem, one level down, for w32-pthreads. +# +# libobs/cmake/os-windows.cmake links `PUBLIC OBS::w32-pthreads`, so +# obs-studio's libobsConfig.cmake.in carries, verbatim: +# +# if(MSVC) +# find_dependency(w32-pthreads REQUIRED) +# endif() +# +# That call is REQUIRED, it runs from inside libobsConfig.cmake, and until +# this block existed it aborted the whole configure the moment the libobs_DIR +# fix above finally succeeded in loading that config file: +# +# By not providing "Findw32-pthreads.cmake" in CMAKE_MODULE_PATH this +# project has asked CMake to find a package configuration file provided by +# "w32-pthreads", but CMake did not find one. +# .deps/cmake/libobs/libobsConfig.cmake:30 (find_dependency) +# +# This is NOT a missing export. deps/w32-pthreads/CMakeLists.txt ends with +# `target_export(w32-pthreads)`, which is the same helper libobs itself uses +# (cmake/common/helpers_common.cmake): it emits install(TARGETS ... EXPORT +# w32-pthreadsTargets), install(EXPORT ... NAMESPACE OBS::), a generated +# w32-pthreadsConfig.cmake and its version file, all COMPONENT Development. +# Those rules *must* exist and *must* have run, for two independent reasons: +# +# 1. CMake hard-errors at generate time if a target in one export set links +# to a target that is in no export set at all ("install(EXPORT +# "libobsTargets") includes target "libobs" which requires target +# "w32-pthreads" that is not in any export set"). obs-studio's generate +# step succeeded, so w32-pthreads was exported. +# 2. The install rules land under the libobs *directory* (this repo's +# _patch_obs_studio_w32_pthreads adds deps/w32-pthreads from +# libobs/CMakeLists.txt), and libobs is the first subdirectory the modern +# top-level CMakeLists.txt adds -- so its whole subtree installs before +# the tolerated UI/obs-frontend-api install error aborts the rest. +# +# The failure is purely the search path, identical to libobs's: target_export +# installs the package to "${OBS_CMAKE_DESTINATION}/${target}", which on +# Windows is /cmake/w32-pthreads/ -- a shape find_package never +# searches. So apply the same remedy CMake's own error message suggests, and +# do it BEFORE find_package(libobs) below, because that is the call that +# transitively triggers find_dependency(w32-pthreads). +# +# macOS and Linux never reach this: the find_dependency is inside `if(MSVC)`, +# and OS_WINDOWS gates the block regardless -- a pure no-op off Windows. +if(OS_WINDOWS AND NOT w32-pthreads_DIR) + set(_stplugin_win_pthreads_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps/cmake/w32-pthreads") + if(EXISTS "${_stplugin_win_pthreads_dir}/w32-pthreadsConfig.cmake") + set(w32-pthreads_DIR "${_stplugin_win_pthreads_dir}" CACHE PATH + "Directory containing w32-pthreadsConfig.cmake" FORCE) + message(STATUS + "w32-pthreads_DIR not set; libobsConfig.cmake's " + "find_dependency(w32-pthreads REQUIRED) hits the same " + "OBS_CMAKE_DESTINATION=cmake search-suffix problem as libobs " + "itself, so pointing it directly at the from-source install: " + "${_stplugin_win_pthreads_dir}") + else() + # Fall back to a hand-written find module rather than letting the + # REQUIRED find_dependency kill the configure. cmake/windows is + # already on CMAKE_MODULE_PATH (see cmake/common/osconfig.cmake), so + # the fallback module deliberately lives in a subdirectory that is + # NOT -- a Findw32-pthreads.cmake sitting on the default module path + # would shadow OBS's own exported package on every build, and its + # real export is the better answer whenever it is present. + # + # Reaching here means the bootstrap install did not produce the + # export, so say exactly what IS in .deps/cmake/ -- the next CI log + # then answers the question directly instead of costing another run. + file(GLOB _stplugin_deps_cmake_dirs "${CMAKE_CURRENT_SOURCE_DIR}/.deps/cmake/*") + message(STATUS + "No w32-pthreadsConfig.cmake at ${_stplugin_win_pthreads_dir}; " + "OBS CMake packages actually installed under .deps/cmake: " + "'${_stplugin_deps_cmake_dirs}'. Falling back to this repo's own " + "Findw32-pthreads.cmake, which builds OBS::w32-pthreads straight " + "from the bootstrap's build artifacts.") + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/find-fallback") + endif() +endif() + find_package(libobs QUIET) +# OBS::w32-pthreads can come back locationless for exactly the same reason +# OBS::libobs can (see the block below this one): the package config loads, +# but the per-configuration w32-pthreadsTargets-release.cmake that carries +# IMPORTED_IMPLIB/IMPORTED_LOCATION may not have been installed. libobs links +# it PUBLIC, so it is on this plugin's own link line and an empty location is +# a generate-time error, not a warning. Repair it from the known install +# destinations (obs-studio/cmake/windows/defaults.cmake: +# OBS_EXECUTABLE_DESTINATION=bin/64bit for the DLL, OBS_LIBRARY_DESTINATION=lib +# for the import library) -- the same repair, and the same reasoning, as the +# OBS::libobs one. +if(OS_WINDOWS AND TARGET OBS::w32-pthreads) + get_target_property(_stplugin_pthreads_implib OBS::w32-pthreads IMPORTED_IMPLIB) + get_target_property(_stplugin_pthreads_implib_release OBS::w32-pthreads IMPORTED_IMPLIB_RELEASE) + if(NOT _stplugin_pthreads_implib AND NOT _stplugin_pthreads_implib_release) + find_file(_stplugin_pthreads_implib_found w32-pthreads.lib + PATHS "${CMAKE_CURRENT_SOURCE_DIR}/.deps/lib" NO_DEFAULT_PATH) + find_file(_stplugin_pthreads_dll_found w32-pthreads.dll + PATHS "${CMAKE_CURRENT_SOURCE_DIR}/.deps/bin/64bit" NO_DEFAULT_PATH) + if(_stplugin_pthreads_implib_found) + set_target_properties(OBS::w32-pthreads PROPERTIES + IMPORTED_IMPLIB "${_stplugin_pthreads_implib_found}") + if(_stplugin_pthreads_dll_found) + set_target_properties(OBS::w32-pthreads PROPERTIES + IMPORTED_LOCATION "${_stplugin_pthreads_dll_found}") + endif() + message(STATUS + "OBS::w32-pthreads had no imported location; pointed it at " + "${_stplugin_pthreads_implib_found}") + endif() + endif() +endif() + # The imported OBS::libobs target can come back without a location. OBS 30.0.2 # installs libobsTargets.cmake but not the per-configuration # libobsTargets-.cmake alongside it when libobs is built on its own, diff --git a/README.md b/README.md index 8cd7193..d9bc777 100644 --- a/README.md +++ b/README.md @@ -458,3 +458,86 @@ pass", which earlier (failing-job) runs did show before failing later in the job. The `w32-pthreads` blocker above is the next thing to solve, and it is upstream's problem to work around rather than a defect in this repo's bootstrap. + +### After the w32-pthreads target: the w32-pthreads *package* + +The `add_subdirectory(deps/w32-pthreads)` patch above did its job — the +`edb0c02`-era generate error is gone, obs-studio 30.0.2 configures, builds +`w32-pthreads.dll` and `obs.dll`, and installs. Two further Windows-only +blockers then surfaced behind it, both the same underlying upstream mismatch +and neither one a defect in this repo: + +1. **`find_package(libobs)` could not locate the package it had just + installed.** obs-studio's `cmake/windows/defaults.cmake` sets + `OBS_CMAKE_DESTINATION=cmake`, and `target_export()` installs each + package to `/${OBS_CMAKE_DESTINATION}//` — i.e. + `.deps/cmake/libobs/`. That is not one of CMake's Config-mode search + suffixes (`/cmake/` is, but only for a config file sitting + *directly* in it; `/*/cmake/` is, but the `` + directory has to come first). Fixed in `79de5e8f` by setting + `libobs_DIR` explicitly; the long comment above that block in + `CMakeLists.txt` has the full reasoning. + +2. **…and then `libobsConfig.cmake` could not locate `w32-pthreads` for + exactly the same reason.** `libobs/cmake/os-windows.cmake` links + `PUBLIC OBS::w32-pthreads`, so upstream's `libobsConfig.cmake.in` carries + a hard `find_dependency(w32-pthreads REQUIRED)` under `if(MSVC)`. Once + fix 1 finally got that config file loaded, the dependency lookup inside + it failed and killed the configure: + + ``` + By not providing "Findw32-pthreads.cmake" in CMAKE_MODULE_PATH this project + has asked CMake to find a package configuration file provided by + "w32-pthreads", but CMake did not find one. + .deps/cmake/libobs/libobsConfig.cmake:30 (find_dependency) + ``` + + **This is not a missing export**, which was the first hypothesis and is + worth recording as wrong: `deps/w32-pthreads/CMakeLists.txt` ends with + `target_export(w32-pthreads)`, the same helper `libobs` itself uses, so + it does emit `install(TARGETS … EXPORT w32-pthreadsTargets)`, + `install(EXPORT … NAMESPACE OBS::)` and a generated + `w32-pthreadsConfig.cmake`, all `COMPONENT Development`. Two independent + arguments say those rules ran: CMake hard-errors at generate time if an + exported target links a target that is in no export set at all (and OBS's + generate step succeeded), and this repo's patch adds `deps/w32-pthreads` + from `libobs/CMakeLists.txt`, making it part of the `libobs` subtree — + which installs *before* the tolerated `UI/obs-frontend-api` install error + aborts the rest. The package really is at `.deps/cmake/w32-pthreads/`; + `find_package` was simply never going to look there. + + So the fix is the same one-liner as for `libobs`, and it is literally + what CMake's own error message suggests: set `w32-pthreads_DIR` before + the `find_package(libobs)` call that transitively triggers the + `find_dependency`. Both `_DIR` blocks now sit next to each other in + `CMakeLists.txt`. + + Behind that sits `cmake/windows/find-fallback/Findw32-pthreads.cmake`, + used only if that export is genuinely absent from `.deps/`. It rebuilds + `OBS::w32-pthreads` by hand from the bootstrap's own artifacts. It + deliberately does *not* live in `cmake/windows/`, which + `cmake/common/osconfig.cmake` already puts on `CMAKE_MODULE_PATH` for + every Windows configure — a find module there would shadow OBS's real + exported package on every build, since `find_package` tries MODULE mode + before CONFIG mode. `CMakeLists.txt` appends the `find-fallback/` + directory to `CMAKE_MODULE_PATH` only after it has established the real + export is missing, and logs what *is* under `.deps/cmake/` when it does, + so a future failure of this shape is answered by the CI log rather than + by another run. + + Note this package is load-bearing for more than the dependency check: + `libobs/util/threading.h` does `#include `, and on Windows + that header only exists because `target_export(w32-pthreads)` installs + `pthread.h`/`sched.h` as `PUBLIC_HEADER` into `.deps/include/`. + +Verified before pushing, on Linux, since this is a Linux sandbox: a +reconstruction of the exact failure — a stub `libobsConfig.cmake` containing +`find_dependency(w32-pthreads REQUIRED)`, reached through `libobs_DIR`, with +the package installed at `.deps/cmake/w32-pthreads/` — reproduces the CI +error without the `w32-pthreads_DIR` block and passes with it; and the +fallback find module was exercised separately by deleting that package, with +`find_package` resolving through MODULE mode to it instead. A full Linux +configure of this repo is unchanged (both blocks are inside `if(OS_WINDOWS)`, +and the upstream `find_dependency` is inside `if(MSVC)`, so macOS and Linux +are pure no-ops). Whether the real Windows runner now gets to a +`streamer-tools-camera.dll` is what the CI run on this commit has to show. diff --git a/cmake/windows/find-fallback/Findw32-pthreads.cmake b/cmake/windows/find-fallback/Findw32-pthreads.cmake new file mode 100644 index 0000000..9b7904a --- /dev/null +++ b/cmake/windows/find-fallback/Findw32-pthreads.cmake @@ -0,0 +1,118 @@ +# Findw32-pthreads.cmake -- last-resort fallback, Windows only. +# +# WHY THIS EXISTS +# +# obs-studio's libobs links `PUBLIC OBS::w32-pthreads` on Windows +# (libobs/cmake/os-windows.cmake), so the libobsConfig.cmake it installs +# carries an unconditional, REQUIRED dependency on a findable `w32-pthreads` +# CMake package: +# +# if(MSVC) +# find_dependency(w32-pthreads REQUIRED) +# endif() +# +# obs-studio *does* export that package -- deps/w32-pthreads/CMakeLists.txt +# ends in `target_export(w32-pthreads)` -- but it exports it to +# /cmake/w32-pthreads/, the same OBS_CMAKE_DESTINATION shape that +# find_package's Config-mode search suffixes never look at. The top-level +# CMakeLists.txt therefore points `w32-pthreads_DIR` straight at it, exactly +# as it already does for `libobs_DIR`, and that is the path this build is +# expected to take. +# +# This module is only reached when that export is genuinely absent from +# .deps/ -- for instance if a future obs-studio changes where or whether +# deps/w32-pthreads installs its package, or if the tolerated +# UI/obs-frontend-api install error in +# cmake/common/buildspec_common.cmake::_setup_obs_studio ever starts aborting +# the install *before* libobs's own subtree instead of after it. In that case +# the alternative is a hard configure failure inside libobsConfig.cmake with +# no plugin module built at all, so reconstructing the imported target by +# hand from artifacts the bootstrap definitely produced is strictly better. +# +# It deliberately lives in cmake/windows/find-fallback/ and NOT in +# cmake/windows/, which cmake/common/osconfig.cmake puts on CMAKE_MODULE_PATH +# for every Windows configure. A Findw32-pthreads.cmake on the default module +# path would win over OBS's own exported package on every single build +# (find_package tries MODULE mode before CONFIG mode), permanently replacing +# upstream's real export metadata with this approximation. The top-level +# CMakeLists.txt appends this directory to CMAKE_MODULE_PATH only when it has +# already established that the real export is missing. +# +# Nothing outside Windows ever loads this: the find_dependency above is +# inside `if(MSVC)`, and the CMAKE_MODULE_PATH append that exposes this file +# is inside `if(OS_WINDOWS ...)`. + +if(TARGET OBS::w32-pthreads) + set(w32-pthreads_FOUND TRUE) + return() +endif() + +# Resolve paths from this file's own location rather than +# CMAKE_CURRENT_SOURCE_DIR: a find module runs in whatever scope called +# find_package, and here that call comes from inside libobsConfig.cmake. +# /cmake/windows/find-fallback/ -> +get_filename_component(_w32pt_repo_root "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) +set(_w32pt_deps "${_w32pt_repo_root}/.deps") + +# Search order, both from obs-studio's own Windows layout: +# 1. the bootstrap's install prefix -- obs-studio/cmake/windows/defaults.cmake +# sets OBS_LIBRARY_DESTINATION=lib (ARCHIVE, i.e. the import library), +# OBS_EXECUTABLE_DESTINATION=bin/64bit (RUNTIME, i.e. the DLL) and +# OBS_INCLUDE_DESTINATION=include (where target_export installs +# w32-pthreads' PUBLIC_HEADER pthread.h/sched.h); +# 2. the obs-studio build tree the bootstrap just built in, in case the +# install step is what failed rather than the export. +file(GLOB _w32pt_build_trees "${_w32pt_deps}/obs-studio-*/build_*/deps/w32-pthreads/Release") +file(GLOB _w32pt_source_trees "${_w32pt_deps}/obs-studio-*/deps/w32-pthreads") + +find_library( + w32-pthreads_IMPLIB + NAMES w32-pthreads + PATHS "${_w32pt_deps}/lib" ${_w32pt_build_trees} + NO_DEFAULT_PATH +) +find_file( + w32-pthreads_RUNTIME_LIBRARY + NAMES w32-pthreads.dll + PATHS "${_w32pt_deps}/bin/64bit" ${_w32pt_build_trees} + NO_DEFAULT_PATH +) +find_path( + w32-pthreads_INCLUDE_DIR + NAMES pthread.h + PATHS "${_w32pt_deps}/include" ${_w32pt_source_trees} + NO_DEFAULT_PATH +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + w32-pthreads + REQUIRED_VARS w32-pthreads_IMPLIB w32-pthreads_INCLUDE_DIR + REASON_FAILURE_MESSAGE + "The OBS SDK bootstrap neither exported a w32-pthreads CMake package under ${_w32pt_deps}/cmake/ nor left a w32-pthreads import library anywhere under ${_w32pt_deps}." +) + +if(w32-pthreads_FOUND) + # SHARED, matching deps/w32-pthreads/CMakeLists.txt's own + # `add_library(w32-pthreads SHARED ...)`. IMPORTED_LOCATION is the DLL and + # IMPORTED_IMPLIB the .lib, which is what an imported SHARED library means + # on Windows; if only the .lib was found, declare it UNKNOWN instead so + # CMake does not demand a DLL it will never be handed. + if(w32-pthreads_RUNTIME_LIBRARY) + add_library(OBS::w32-pthreads SHARED IMPORTED) + set_target_properties( + OBS::w32-pthreads + PROPERTIES IMPORTED_LOCATION "${w32-pthreads_RUNTIME_LIBRARY}" IMPORTED_IMPLIB "${w32-pthreads_IMPLIB}" + ) + else() + add_library(OBS::w32-pthreads UNKNOWN IMPORTED) + set_target_properties(OBS::w32-pthreads PROPERTIES IMPORTED_LOCATION "${w32-pthreads_IMPLIB}") + endif() + set_target_properties( + OBS::w32-pthreads + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${w32-pthreads_INCLUDE_DIR}" + ) + message(STATUS "Findw32-pthreads (fallback): OBS::w32-pthreads -> ${w32-pthreads_IMPLIB}") +endif() + +mark_as_advanced(w32-pthreads_IMPLIB w32-pthreads_RUNTIME_LIBRARY w32-pthreads_INCLUDE_DIR)