Skip to content

Updated scripts for Windows build #73

Updated scripts for Windows build

Updated scripts for Windows build #73

Workflow file for this run

name: Build revo-lib with OBS 32.x
on:
workflow_dispatch:
push:
branches: [ main ]
jobs:
build-macos:
name: Build (macOS)
runs-on: macos-15-intel
env:
MACOSX_DEPLOYMENT_TARGET: "13.0"
CMAKE_OSX_DEPLOYMENT_TARGET: "13.0"
RUST_BACKTRACE: "1"
# build.rs używa tego jako ref (domyślnie 32.1.0-rc1 jeśli brak)
REVO_OBS_REF: "32.1.0-rc1"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: |
brew update
brew install simde ffmpeg mbedtls speexdsp uthash jansson cmake ninja pkg-config
- name: Build (revo-lib) + build.rs builds OBS
shell: bash
run: |
set -euo pipefail
cargo build --release
- name: Package prebuilt libobs artifact (layout compatible with REVO_PREBUILT_LIBOBS_DIR)
shell: bash
run: |
set -euo pipefail
ARCH=$(uname -m)
STAGING="libobs-prebuilt-macos-${ARCH}"
OBS_SRC="obs-libobs/obs-studio"
BUILD_DIR="${OBS_SRC}/build-headless"
INSTALL_PREFIX="${BUILD_DIR}/install"
mkdir -p "${STAGING}/framework"
mkdir -p "${STAGING}/include/obs"
mkdir -p "${STAGING}/bindings"
# 1) Framework
FW_DIR="$(find "${BUILD_DIR}" -type d -name "libobs.framework" -print -quit || true)"
if [ -z "${FW_DIR}" ]; then
echo "ERROR: libobs.framework not found under ${BUILD_DIR}" >&2
find "${BUILD_DIR}" -maxdepth 8 -name "*.framework" -print || true
exit 1
fi
# Copy as obs.framework to match build.rs prebuilt link name (framework=obs)
rm -rf "${STAGING}/framework/obs.framework"
cp -R "${FW_DIR}" "${STAGING}/framework/obs.framework"
echo "Copied framework: ${FW_DIR} -> ${STAGING}/framework/obs.framework"
# Ensure the binary name matches what build.rs links: framework=obs
if [ -f "${STAGING}/framework/obs.framework/libobs" ] && [ ! -e "${STAGING}/framework/obs.framework/obs" ]; then
ln -sf "libobs" "${STAGING}/framework/obs.framework/obs"
fi
# 2) Headers
if [ -d "${INSTALL_PREFIX}/include/obs" ]; then
cp -R "${INSTALL_PREFIX}/include/obs/." "${STAGING}/include/obs/"
echo "Copied installed headers from ${INSTALL_PREFIX}/include/obs"
fi
cp "${OBS_SRC}/libobs/"*.h "${STAGING}/include/obs/" 2>/dev/null || true
# obsconfig.h
if [ -f "${BUILD_DIR}/config/obsconfig.h" ]; then
cp "${BUILD_DIR}/config/obsconfig.h" "${STAGING}/include/obs/"
echo "Copied obsconfig.h"
else
echo "WARNING: obsconfig.h not found at ${BUILD_DIR}/config/obsconfig.h" >&2
fi
# 3) Bindings (pre-generated)
BINDINGS="$(find target -path "*/out/libobs_bindings.rs" -print -quit || true)"
if [ -z "${BINDINGS}" ]; then
echo "ERROR: libobs_bindings.rs not found in target/**/out/" >&2
find target -maxdepth 6 -name "libobs_bindings.rs" -print || true
exit 1
fi
cp "${BINDINGS}" "${STAGING}/bindings/libobs_bindings.rs"
echo "Copied bindings from ${BINDINGS}"
# 4) Metadata
echo "REVO_OBS_REF=${REVO_OBS_REF}" >> "${STAGING}/build-info.env"
echo "OBS_GIT=$(git -C ${OBS_SRC} describe --tags --always 2>/dev/null || echo unknown)" >> "${STAGING}/build-info.env"
echo "BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "${STAGING}/build-info.env"
echo "ARCH=${ARCH}" >> "${STAGING}/build-info.env"
echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" >> "${STAGING}/build-info.env"
echo "Artifact layout:"
find "${STAGING}" -not -name '*.h' | sort
echo "STAGING=${STAGING}" >> "$GITHUB_ENV"
- name: Patch framework install names (make artifact portable)
shell: bash
run: |
set -euo pipefail
FW_DIR="${{ env.STAGING }}/framework/obs.framework"
# If obs doesn't exist for some reason, create it now
if [ -f "${FW_DIR}/libobs" ] && [ ! -e "${FW_DIR}/obs" ]; then
ln -sf "libobs" "${FW_DIR}/obs"
fi
# Patch BOTH names if present, so whichever the linker uses is consistent.
for FW_BIN in "${FW_DIR}/libobs" "${FW_DIR}/obs"; do
if [ -f "${FW_BIN}" ]; then
echo "== Patching: ${FW_BIN} =="
echo "Before:"
otool -D "${FW_BIN}" || true
otool -L "${FW_BIN}" || true
install_name_tool -id "@rpath/obs.framework/libobs" "${FW_BIN}"
echo "After:"
otool -D "${FW_BIN}" || true
otool -L "${FW_BIN}" || true
echo "== LC_BUILD_VERSION =="
otool -l "${FW_BIN}" | grep -A2 LC_BUILD_VERSION || true
echo
fi
done
# Re-sign after modifications
codesign --force --sign - "${FW_DIR}" || true
- name: CALCULATED BUNDLE SIZE (system deps referenced by obs.framework)
shell: bash
run: |
set -euo pipefail
STAGING="${{ env.STAGING }}"
FW_DIR="${STAGING}/framework/obs.framework"
if [ -f "${FW_DIR}/obs" ]; then
FW_BIN="${FW_DIR}/obs"
else
FW_BIN="${FW_DIR}/libobs"
fi
echo "=============================="
echo "CALCULATED BUNDLE SIZE (SYSTEM DEPS)"
echo "=============================="
echo "Framework dir: ${FW_DIR}"
echo "Framework bin: ${FW_BIN}"
echo
echo "[STAGING TOTAL]"
du -sh "${STAGING}" || true
echo
echo "[FRAMEWORK TOTAL]"
du -sh "${FW_DIR}" || true
echo
echo "[BREW INFO]"
command -v brew || true
BREW_PREFIX="$(brew --prefix 2>/dev/null || true)"
echo "brew --prefix: ${BREW_PREFIX}"
echo "Exists /usr/local/opt? $(test -d /usr/local/opt && echo yes || echo no)"
echo "Exists /opt/homebrew/opt? $(test -d /opt/homebrew/opt && echo yes || echo no)"
echo
echo "[DEPENDENCIES FROM otool -L]"
DEPS_FILE="$(mktemp)"
otool -L "${FW_BIN}" \
| tail -n +2 \
| awk '{print $1}' \
| grep -vE '^(@rpath|@loader_path|@executable_path)' \
| grep -E '^/' \
| sort -u > "${DEPS_FILE}" || true
sed 's/^/ - /' "${DEPS_FILE}" || true
echo
echo "[CHECK IF otool PATHS EXIST ON DISK]"
while IFS= read -r p; do
[ -n "${p}" ] || continue
if [ -e "${p}" ]; then
echo "OK ${p}"
else
echo "MISS ${p}"
fi
done < "${DEPS_FILE}"
echo
echo "[FFMPEG SIZE (by brew prefix)]"
if brew --prefix ffmpeg >/dev/null 2>&1; then
FFMPEG_PREFIX="$(brew --prefix ffmpeg)"
echo "ffmpeg prefix: ${FFMPEG_PREFIX}"
echo "ffmpeg lib dir size: "
du -sh "${FFMPEG_PREFIX}/lib" || true
echo "ffmpeg dylibs:"
ls -lh "${FFMPEG_PREFIX}/lib"/libav*.dylib 2>/dev/null || true
echo "ffmpeg dylib total:"
python3 - <<'PY'
import glob, os
paths = glob.glob(os.path.join(os.environ["FFMPEG_PREFIX"], "lib", "libav*.dylib"))
total = sum(os.path.getsize(p) for p in paths if os.path.exists(p))
print(f"{total/1024/1024:.2f} MB")
PY
else
echo "ffmpeg not installed (brew --prefix ffmpeg failed)"
fi
echo
echo "[JANSSON SIZE (by brew prefix)]"
if brew --prefix jansson >/dev/null 2>&1; then
JANSSON_PREFIX="$(brew --prefix jansson)"
echo "jansson prefix: ${JANSSON_PREFIX}"
ls -lh "${JANSSON_PREFIX}/lib"/libjansson*.dylib 2>/dev/null || true
python3 - <<'PY'
import glob, os
paths = glob.glob(os.path.join(os.environ["JANSSON_PREFIX"], "lib", "libjansson*.dylib"))
total = sum(os.path.getsize(p) for p in paths if os.path.exists(p))
print(f"{total/1024/1024:.2f} MB")
PY
else
echo "jansson not installed (brew --prefix jansson failed)"
fi
echo
rm -f "${DEPS_FILE}"
env:
FFMPEG_PREFIX: ""
JANSSON_PREFIX: ""
- name: Upload libobs prebuilt artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.STAGING }}
path: ${{ env.STAGING }}/
if-no-files-found: error
build-windows:
name: Build (Windows)
runs-on: windows-2025
env:
RUST_BACKTRACE: "1"
REVO_OBS_REF: "32.1.0-rc1"
# vcpkg: prefer Release only
VCPKG_DEFAULT_TRIPLET: "x64-windows-release"
VCPKG_BUILD_TYPE: "release"
# Optional: encourage single-config Release for CMake projects that respect it
CMAKE_BUILD_TYPE: "Release"
CMAKE_GENERATOR: "Ninja"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Select Windows SDK (>= 10.0.20348.0)
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$kitsRoot = "C:\Program Files (x86)\Windows Kits\10\Lib"
if (-not (Test-Path $kitsRoot)) {
throw "Windows Kits not found at: $kitsRoot"
}
$all = Get-ChildItem -Directory $kitsRoot | Select-Object -ExpandProperty Name
Write-Host "Installed Windows SDK entries:"
$all | Sort-Object | ForEach-Object { Write-Host " - $_" }
# Keep only semantic SDK version folders like 10.0.22621.0, skip 'wdf'
$versions = $all | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' }
if (-not $versions -or $versions.Count -eq 0) {
throw "No Windows SDK version folders found under: $kitsRoot"
}
$selected = ($versions | Sort-Object {[version]$_} | Select-Object -Last 1).Trim()
Write-Host "Selected Windows SDK version: $selected"
if ([version]$selected -lt [version]"10.0.20348.0") {
throw "Selected SDK $selected is < 10.0.20348.0 (OBS requirement)."
}
# Tooling typically expects trailing backslash
"WindowsSDKVersion=$selected\" | Out-File -Append -Encoding utf8 $env:GITHUB_ENV
"CMAKE_SYSTEM_VERSION=$selected" | Out-File -Append -Encoding utf8 $env:GITHUB_ENV
- name: Install Ninja
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
choco install -y ninja
ninja --version
- name: Install dependencies (vcpkg, classic mode, Release only)
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
if (-not $env:VCPKG_INSTALLATION_ROOT -or $env:VCPKG_INSTALLATION_ROOT.Trim() -eq "") {
$env:VCPKG_INSTALLATION_ROOT = "C:\vcpkg"
}
$vcpkgRoot = $env:VCPKG_INSTALLATION_ROOT
Write-Host "Using vcpkg root: $vcpkgRoot"
if (-not (Test-Path $vcpkgRoot)) {
throw "vcpkg root does not exist: $vcpkgRoot"
}
$vcpkgExe = Join-Path $vcpkgRoot "vcpkg.exe"
if (-not (Test-Path $vcpkgExe)) {
Write-Host "vcpkg.exe not found, bootstrapping..."
Push-Location $vcpkgRoot
.\bootstrap-vcpkg.bat
Pop-Location
}
& $vcpkgExe version
# Release-only triplets (avoid building debug variants)
& $vcpkgExe install `
ffmpeg:x64-windows-release `
zlib:x64-windows-release `
mbedtls:x64-windows-release `
speexdsp:x64-windows-release `
jansson:x64-windows-release `
uthash:x64-windows-release `
simde:x64-windows-release
# Export VCPKG_ROOT for subsequent steps (build.rs reads it for toolchain)
"VCPKG_ROOT=$vcpkgRoot" | Out-File -Append -Encoding utf8 $env:GITHUB_ENV
Write-Host "Exported VCPKG_ROOT=$vcpkgRoot"
- name: Build (revo-lib) + build.rs builds OBS
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
# VCPKG_ROOT is written to GITHUB_ENV by the install step above;
# it is already available as a process env var here.
Write-Host "VCPKG_ROOT=$env:VCPKG_ROOT"
Write-Host "CMAKE_SYSTEM_VERSION=$env:CMAKE_SYSTEM_VERSION"
if (-not $env:VCPKG_ROOT -or -not (Test-Path $env:VCPKG_ROOT)) {
throw "VCPKG_ROOT ('$env:VCPKG_ROOT') is not set or does not exist."
}
$env:VCPKG_DEFAULT_TRIPLET = "x64-windows-release"
cargo build --release
- name: Package artifact (prebuilt layout)
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$arch = "x86_64"
$staging = "libobs-prebuilt-windows-$arch"
$obsSrc = "obs-libobs\obs-studio"
$buildDir = "$obsSrc\build-headless"
$installDir = "$buildDir\install"
New-Item -ItemType Directory -Force -Path "$staging\lib" | Out-Null
New-Item -ItemType Directory -Force -Path "$staging\bin" | Out-Null
New-Item -ItemType Directory -Force -Path "$staging\include\obs" | Out-Null
New-Item -ItemType Directory -Force -Path "$staging\bindings" | Out-Null
# 1) Import lib (obs.lib) – searched by rustc
$obsLib = Get-ChildItem -Recurse "$buildDir" -Filter "obs.lib" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($obsLib) {
Copy-Item $obsLib.FullName "$staging\lib\obs.lib"
Write-Host "Copied obs.lib from $($obsLib.FullName)"
} else {
Write-Warning "obs.lib not found under $buildDir"
}
# 2) DLL (obs.dll) – needed at runtime
$obsDll = Get-ChildItem -Recurse "$buildDir" -Filter "obs.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($obsDll) {
Copy-Item $obsDll.FullName "$staging\bin\obs.dll"
Write-Host "Copied obs.dll from $($obsDll.FullName)"
} else {
Write-Warning "obs.dll not found under $buildDir"
}
# 3) Headers
if (Test-Path "$installDir\include\obs") {
Copy-Item -Recurse "$installDir\include\obs\*" "$staging\include\obs\"
Write-Host "Copied installed headers from $installDir\include\obs"
}
Get-ChildItem "$obsSrc\libobs" -Filter "*.h" | ForEach-Object {
Copy-Item $_.FullName "$staging\include\obs\" -Force
}
# obsconfig.h
$obscfg = Get-ChildItem -Recurse "$buildDir" -Filter "obsconfig.h" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($obscfg) {
Copy-Item $obscfg.FullName "$staging\include\obs\obsconfig.h" -Force
Write-Host "Copied obsconfig.h from $($obscfg.FullName)"
} else {
Write-Warning "obsconfig.h not found"
}
# 4) Bindings
$bindings = Get-ChildItem -Recurse "target" -Filter "libobs_bindings.rs" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($bindings) {
Copy-Item $bindings.FullName "$staging\bindings\libobs_bindings.rs"
Write-Host "Copied bindings from $($bindings.FullName)"
} else {
Write-Error "libobs_bindings.rs not found in target\**\out\"
exit 1
}
# 5) Metadata
"REVO_OBS_REF=$env:REVO_OBS_REF" | Out-File -Append -Encoding ascii "$staging\build-info.env"
"BUILD_DATE=$(Get-Date -AsUTC -Format o)" | Out-File -Append -Encoding ascii "$staging\build-info.env"
"ARCH=$arch" | Out-File -Append -Encoding ascii "$staging\build-info.env"
Write-Host "Artifact layout:"
Get-ChildItem -Recurse $staging | Where-Object { -not $_.Name.EndsWith(".h") } | Sort-Object FullName | ForEach-Object { Write-Host $_.FullName }
"STAGING=$staging" | Out-File -Append -Encoding utf8 $env:GITHUB_ENV
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.STAGING }}
path: ${{ env.STAGING }}\
if-no-files-found: error