From 689aa160eeef9fdfc975ef90f12fe6de716acef9 Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Sun, 1 Mar 2026 22:43:25 +0100 Subject: [PATCH 1/7] RPS reframing --- DEVELOPMENT.md | 2 +- TODO.md | 45 +++++++++++++++++++++++++++++++++++++++++ examples/java/README.md | 2 +- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 TODO.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 95cc2f9..5b9f107 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,6 +1,6 @@ # RPS Developer Guide -Welcome to the **Reliable Plugin Scanner (RPS)** project! This guide explains the architecture, design choices, and implementation details of what we have built so far, serving as an onboarding manual for new developers. +Welcome to the **RPS** project! This guide explains the architecture, design choices, and implementation details of what we have built so far, serving as an onboarding manual for new developers. --- diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..6f089df --- /dev/null +++ b/TODO.md @@ -0,0 +1,45 @@ +# RPS - TODO List + +This document tracks identified architectural improvements and known bugs across the project, ordered by priority. + +## 🔴 Critical Priority (Bugs & Stability) + +### 1. Fix `EXC_BAD_ACCESS` (Double-Free) in AudioUnit Scanner +- **File:** `apps/rps-pluginscanner/src/scanners/AuScanner.mm` +- **Description:** Around line 191, the code calls `CFRelease(paramInfo.cfNameString);`. Under CoreAudio's memory management rules (the "Get Rule"), the caller does not own this string reference. Releasing it causes an over-release, which can lead to hard crashes when scanning well-behaved Audio Units. +- **Action:** Remove the `CFRelease` call. + +## 🟠 High Priority (Architecture & Performance) + +### 2. Replace Internal IPC Mechanism (JSON/Named Queues -> Protobuf/Anonymous Pipes) +- **Files:** `libs/rps-ipc/src/Connection.cpp`, `libs/rps-ipc/include/rps/ipc/Connection.hpp` +- **Description:** Currently, communication between the Orchestrator and the Worker processes uses `boost::interprocess::message_queue` combined with Boost.JSON serialization. + - **Issue 1:** Named queues can remain orphaned in the OS if the orchestrator is hard-killed (e.g., `SIGKILL`), causing conflicts on subsequent runs. + - **Issue 2:** JSON serialization is relatively slow and memory-intensive for large plugins with thousands of parameters. +- **Action:** Since the project already uses Protobuf for the external gRPC API, switch the internal IPC to also use Protobuf (or FlatBuffers). Migrate the transport layer from named queues to anonymous pipes (Standard I/O streams using `boost::process`) to guarantee OS-level cleanup upon process termination. + +## 🟡 Medium Priority (Enhancements) + +### 3. Exhaustive AudioUnit Parameter Extraction +- **File:** `apps/rps-pluginscanner/src/scanners/AuScanner.mm` +- **Description:** The AU scanner currently only queries `kAudioUnitScope_Global` for parameters. Many plugins (especially multi-bus effects and synths) register automatable parameters under `kAudioUnitScope_Input` or `kAudioUnitScope_Output`. +- **Action:** Iterate over input/output scopes and their respective buses to ensure all plugin parameters are captured. + +### 4. Dynamic AudioUnit Registration for Arbitrary Paths +- **File:** `apps/rps-pluginscanner/src/scanners/AuScanner.mm` +- **Description:** `AudioComponentFindNext` only searches for components that have already been registered by macOS (typically residing in `/Library/Audio/Plug-Ins/Components`). If a user attempts to scan an unregistered `.component` bundle located elsewhere (e.g., `~/Downloads/`), the instantiation will fail. +- **Action:** If `AudioComponentFindNext` fails, dynamically register the bundle for the duration of the process using `AudioComponentRegister()` before attempting to find it again. + +### 5. FourCC Parsing Fallback in AU `Info.plist` +- **File:** `apps/rps-pluginscanner/src/scanners/AuScanner.mm` +- **Description:** The `fourccToUInt32` lambda expects the string representation of a type/subtype to be exactly 4 characters long. Some legacy or poorly-formed plugins provide these codes as raw base-10 integers in the `Info.plist`. In these cases, the string conversion yields a numeric string (e.g., `"1096107074"`), which fails the length check. +- **Action:** Add a fallback mechanism to check if the string can be parsed as a raw integer (e.g., `[str longLongValue]`) if the length is not 4. + +## 🔵 Epic / Long-Term Vision + +### 6. Expand to Out-of-Process Plugin Engine (RPE) +- **Description:** Evolve the scanner into a full execution engine. Allow clients (via gRPC) to not only scan plugins but remotely instantiate them, stream parameter changes, and process audio. +- **Implementation Notes:** + - **Audio Data Plane:** Do not use gRPC for real-time audio buffer streaming due to jitter/latency. Establish a Shared Memory Ring Buffer (local) or fast UDP socket (network) negotiated via gRPC. + - **UI Management:** Use SDL3 in the worker process to spawn floating UI windows for the plugins. Avoid attempting complex OS-level window reparenting/embedding initially. + - **Plugin Chains:** Ensure entire effects chains (e.g., EQ -> Compressor) are hosted within a single worker process to prevent IPC context-switching overhead during the audio loop. diff --git a/examples/java/README.md b/examples/java/README.md index 0057649..5c0dfb1 100644 --- a/examples/java/README.md +++ b/examples/java/README.md @@ -1,6 +1,6 @@ # RPS Java Client Example -This is a professional Java client for the Reliable Plugin Scanner (RPS) gRPC service. It demonstrates how to connect to the server, start a scan, and handle streaming scan events using a modern Terminal UI (TUI). +This is a professional Java client for the RPS gRPC service. It demonstrates how to connect to the server, start a scan, and handle streaming scan events using a modern Terminal UI (TUI). ## Prerequisites From e068e7199ba0fc9a151c9a294d9585ffbb13afac Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 21:28:01 +0100 Subject: [PATCH 2/7] updated github workflow. --- .github/workflows/build.yml | 466 +++++++++++++++++++----------------- 1 file changed, 250 insertions(+), 216 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b32b334..4ef7e64 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,231 +11,265 @@ jobs: fail-fast: false matrix: include: + # Linux - os: ubuntu-latest name: Linux x64 toolchain: clang + artifact_name: rps-linux-x64 + archive_ext: tar.gz + vcpkg_triplet: x64-linux + + - os: ubuntu-24.04-arm + name: Linux arm64 + toolchain: clang + artifact_name: rps-linux-arm64 + archive_ext: tar.gz + vcpkg_triplet: arm64-linux + + # macOS + - os: macos-15-large + name: macOS Intel + toolchain: clang + artifact_name: rps-macos-intel + archive_ext: zip + vcpkg_triplet: x64-osx-release + - os: macos-15 name: macOS Apple Silicon toolchain: clang -# - os: windows-latest -# name: Windows MSVC -# toolchain: msvc + artifact_name: rps-macos-arm64 + archive_ext: zip + vcpkg_triplet: arm64-osx-release + + # Windows - os: windows-latest - name: Windows Clang + name: Windows Clang x64 toolchain: clang + artifact_name: rps-win-x64 + archive_ext: zip + vcpkg_triplet: x64-windows-static + msvc_arch: x64 + + - os: windows-latest + name: Windows Clang ARM64 + toolchain: clang + artifact_name: rps-win-arm64 + archive_ext: zip + vcpkg_triplet: arm64-windows-static + msvc_arch: x64_arm64 steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Build Tools (Linux) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y cmake ninja-build g++ clang pkg-config - - - name: Install Build Tools (macOS) - if: runner.os == 'macOS' - run: | - brew install cmake ninja pkg-config - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ runner.os }}-${{ matrix.toolchain }}-ccache - - - name: Setup MSVC Developer Command Prompt - if: runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 - - - name: Select Windows storage for vcpkg - if: runner.os == 'Windows' - id: win_storage - shell: pwsh - run: | - $candidates = @() - foreach ($p in @($env:RUNNER_TEMP, $env:GITHUB_WORKSPACE, 'D:\', 'C:\')) { - if (-not $p) { continue } - if (-not (Test-Path $p)) { continue } - $resolved = (Resolve-Path $p).Path - $qualifier = Split-Path -Qualifier $resolved - $driveName = $qualifier.TrimEnd('\').TrimEnd(':') - $drive = Get-PSDrive -Name $driveName -ErrorAction SilentlyContinue - if ($drive) { - $candidates += [pscustomobject]@{ - Root = $qualifier - Free = [int64]$drive.Free + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Build Tools (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build g++ clang pkg-config + + - name: Install Build Tools (macOS) + if: runner.os == 'macOS' + run: | + brew install cmake ninja pkg-config + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ runner.os }}-${{ matrix.toolchain }}-ccache + + - name: Setup MSVC Developer Command Prompt + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.msvc_arch || 'x64' }} + + - name: Select Windows storage for vcpkg + if: runner.os == 'Windows' + id: win_storage + shell: pwsh + run: | + $candidates = @() + foreach ($p in @($env:RUNNER_TEMP, $env:GITHUB_WORKSPACE, 'D:\', 'C:\')) { + if (-not $p) { continue } + if (-not (Test-Path $p)) { continue } + $resolved = (Resolve-Path $p).Path + $qualifier = Split-Path -Qualifier $resolved + $driveName = $qualifier.TrimEnd('\').TrimEnd(':') + $drive = Get-PSDrive -Name $driveName -ErrorAction SilentlyContinue + if ($drive) { + $candidates += [pscustomobject]@{ + Root = $qualifier + Free = [int64]$drive.Free + } } } - } - - if (-not $candidates) { - throw "Could not determine a writable storage location for vcpkg." - } - - $best = $candidates | Sort-Object Free -Descending | Select-Object -First 1 - $base = Join-Path $best.Root "vcpkg-cache" - - $archives = Join-Path $base "archives" - $downloads = Join-Path $base "downloads" - $buildtrees = Join-Path $base "buildtrees" - $packages = Join-Path $base "packages" - - New-Item -ItemType Directory -Force -Path $archives, $downloads, $buildtrees, $packages | Out-Null - - "VCPKG_DEFAULT_BINARY_CACHE=$archives" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "VCPKG_DOWNLOADS=$downloads" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - "vcpkg_cache_dir=$archives" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - "vcpkg_downloads_dir=$downloads" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - - # --- THE FIX: Create Junctions to redirect C: to D: --- - # 1. Remove existing directories if GitHub pre-created them - Remove-Item -Path "C:\vcpkg\buildtrees" -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path "C:\vcpkg\packages" -Recurse -Force -ErrorAction SilentlyContinue - - # 2. Create the junctions - New-Item -ItemType Junction -Path "C:\vcpkg\buildtrees" -Target $buildtrees | Out-Null - New-Item -ItemType Junction -Path "C:\vcpkg\packages" -Target $packages | Out-Null - - # --- PROOF OF REDIRECTION --- - Write-Host "Testing the Junctions..." - Set-Content -Path "C:\vcpkg\buildtrees\proof.txt" -Value "This is actually on the D drive!" - if (Test-Path "$buildtrees\proof.txt") { - Write-Host "SUCCESS: C:\vcpkg\buildtrees is successfully routing to the D: drive!" - } else { - Write-Error "FAILURE: Junction didn't work." - } - - $bestGb = [math]::Round($best.Free / 1GB, 2) - Write-Host "Selected vcpkg storage root: $($best.Root)" - Write-Host "Selected drive free space: $bestGb GB" - - - name: Restore vcpkg binary archives (Unix) - if: runner.os != 'Windows' - id: vcpkg_cache_restore_unix - uses: actions/cache/restore@v4 - with: - path: ~/.cache/vcpkg/archives - key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} - restore-keys: | - vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}- - - - name: Restore vcpkg binary archives (Windows) - if: runner.os == 'Windows' - id: vcpkg_cache_restore_windows - uses: actions/cache/restore@v4 - with: - path: ${{ steps.win_storage.outputs.vcpkg_cache_dir }} - key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} - restore-keys: | - vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}- - - - name: Ensure vcpkg baseline commit is available (Unix) - if: runner.os != 'Windows' - run: | - BASELINE=$(python3 -c "import json; print(json.load(open('vcpkg.json', encoding='utf-8'))['builtin-baseline'])") - git -C "$VCPKG_INSTALLATION_ROOT" fetch --no-tags origin "$BASELINE" || git -C "$VCPKG_INSTALLATION_ROOT" fetch --tags --prune origin - git -C "$VCPKG_INSTALLATION_ROOT" cat-file -e "${BASELINE}^{commit}" - - - name: Ensure vcpkg baseline commit is available (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - $baseline = (Get-Content vcpkg.json | ConvertFrom-Json).'builtin-baseline' - git -C "$env:VCPKG_INSTALLATION_ROOT" fetch --no-tags origin $baseline - if ($LASTEXITCODE -ne 0) { - git -C "$env:VCPKG_INSTALLATION_ROOT" fetch --tags --prune origin - } - git -C "$env:VCPKG_INSTALLATION_ROOT" cat-file -e "$baseline`^{commit}" - - - name: Configure CMake (Unix) - if: runner.os != 'Windows' - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - TRIPLET="x64-linux" # vcpkg's x64-linux defaults to static libraries - else - TRIPLET="arm64-osx-release" # Force static linking on macOS - fi - - cmake -G Ninja -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DCMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" \ - -DVCPKG_TARGET_TRIPLET=$TRIPLET - - - name: Save vcpkg binary archives (Unix) - if: ${{ runner.os != 'Windows' && always() && steps.vcpkg_cache_restore_unix.outputs.cache-hit != 'true' }} - uses: actions/cache/save@v4 - with: - path: ~/.cache/vcpkg/archives - key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} - - - name: Build (Unix) - if: runner.os != 'Windows' - run: cmake --build build --config Release - - - name: Configure CMake (Windows MSVC) - if: runner.os == 'Windows' && matrix.toolchain == 'msvc' - run: cmake -G Ninja -B build -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DRPS_MSVC_STATIC_RUNTIME=ON - - - name: Configure CMake (Windows Clang) - if: runner.os == 'Windows' && matrix.toolchain == 'clang' - run: cmake -G Ninja -B build -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DRPS_MSVC_STATIC_RUNTIME=ON - - - name: Save vcpkg binary archives (Windows) - if: ${{ runner.os == 'Windows' && always() && steps.vcpkg_cache_restore_windows.outputs.cache-hit != 'true' }} - uses: actions/cache/save@v4 - with: - path: ${{ steps.win_storage.outputs.vcpkg_cache_dir }} - key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} - - - name: Build (Windows) - if: runner.os == 'Windows' - run: cmake --build build --config Release - - - name: Stage & Zip Binaries (Unix) - if: runner.os != 'Windows' - run: | - mkdir -p release_stage - cp build/apps/rps-server/rps-server release_stage/ - cp build/apps/rps-standalone/rps-standalone release_stage/ - cp build/apps/rps-pluginscanner/rps-pluginscanner release_stage/ - cp build/apps/rps-vstscannermaster/vstscannermaster release_stage/ - cp build/examples/cpp/rps-example-client release_stage/ - cd release_stage - zip -r "../rps-binaries-${{ matrix.name }}.zip" ./* - - - name: Stage & Zip Binaries (Windows MSVC) - if: runner.os == 'Windows' && matrix.toolchain == 'msvc' - run: | - New-Item -ItemType Directory -Force -Path release_stage - Copy-Item build\apps\rps-server\rps-server.exe release_stage\ - Copy-Item build\apps\rps-standalone\rps-standalone.exe release_stage\ - Copy-Item build\apps\rps-pluginscanner\rps-pluginscanner.exe release_stage\ - Copy-Item build\apps\rps-vstscannermaster\vstscannermaster.exe release_stage\ - Copy-Item build\examples\cpp\rps-example-client.exe release_stage\ - Compress-Archive -Path release_stage\* -DestinationPath "rps-binaries-${{ matrix.name }}.zip" - - - name: Stage & Zip Binaries (Windows Clang) - if: runner.os == 'Windows' && matrix.toolchain == 'clang' - run: | - New-Item -ItemType Directory -Force -Path release_stage - Copy-Item build\apps\rps-server\rps-server.exe release_stage\ - Copy-Item build\apps\rps-standalone\rps-standalone.exe release_stage\ - Copy-Item build\apps\rps-pluginscanner\rps-pluginscanner.exe release_stage\ - Copy-Item build\apps\rps-vstscannermaster\vstscannermaster.exe release_stage\ - Copy-Item build\examples\cpp\rps-example-client.exe release_stage\ - Compress-Archive -Path release_stage\* -DestinationPath "rps-binaries-${{ matrix.name }}.zip" - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: rps-binaries-${{ matrix.name }} - path: rps-binaries-${{ matrix.name }}.zip - retention-days: 7 + + if (-not $candidates) { + throw "Could not determine a writable storage location for vcpkg." + } + + $best = $candidates | Sort-Object Free -Descending | Select-Object -First 1 + $base = Join-Path $best.Root "vcpkg-cache" + + $archives = Join-Path $base "archives" + $downloads = Join-Path $base "downloads" + $buildtrees = Join-Path $base "buildtrees" + $packages = Join-Path $base "packages" + + New-Item -ItemType Directory -Force -Path $archives, $downloads, $buildtrees, $packages | Out-Null + + "VCPKG_DEFAULT_BINARY_CACHE=$archives" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "VCPKG_DOWNLOADS=$downloads" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + "vcpkg_cache_dir=$archives" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + "vcpkg_downloads_dir=$downloads" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + + # --- THE FIX: Create Junctions to redirect C: to D: --- + # 1. Remove existing directories if GitHub pre-created them + Remove-Item -Path "C:\vcpkg\buildtrees" -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path "C:\vcpkg\packages" -Recurse -Force -ErrorAction SilentlyContinue + + # 2. Create the junctions + New-Item -ItemType Junction -Path "C:\vcpkg\buildtrees" -Target $buildtrees | Out-Null + New-Item -ItemType Junction -Path "C:\vcpkg\packages" -Target $packages | Out-Null + + # --- PROOF OF REDIRECTION --- + Write-Host "Testing the Junctions..." + Set-Content -Path "C:\vcpkg\buildtrees\proof.txt" -Value "This is actually on the D drive!" + if (Test-Path "$buildtrees\proof.txt") { + Write-Host "SUCCESS: C:\vcpkg\buildtrees is successfully routing to the D: drive!" + } else { + Write-Error "FAILURE: Junction didn't work." + } + + $bestGb = [math]::Round($best.Free / 1GB, 2) + Write-Host "Selected vcpkg storage root: $($best.Root)" + Write-Host "Selected drive free space: $bestGb GB" + + - name: Restore vcpkg binary archives (Unix) + if: runner.os != 'Windows' + id: vcpkg_cache_restore_unix + uses: actions/cache/restore@v4 + with: + path: ~/.cache/vcpkg/archives + key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} + restore-keys: | + vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}- + + - name: Restore vcpkg binary archives (Windows) + if: runner.os == 'Windows' + id: vcpkg_cache_restore_windows + uses: actions/cache/restore@v4 + with: + path: ${{ steps.win_storage.outputs.vcpkg_cache_dir }} + key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} + restore-keys: | + vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}- + + - name: Ensure vcpkg baseline commit is available (Unix) + if: runner.os != 'Windows' + run: | + BASELINE=$(python3 -c "import json; print(json.load(open('vcpkg.json', encoding='utf-8'))['builtin-baseline'])") + git -C "$VCPKG_INSTALLATION_ROOT" fetch --no-tags origin "$BASELINE" || git -C "$VCPKG_INSTALLATION_ROOT" fetch --tags --prune origin + git -C "$VCPKG_INSTALLATION_ROOT" cat-file -e "${BASELINE}^{commit}" + + - name: Ensure vcpkg baseline commit is available (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $baseline = (Get-Content vcpkg.json | ConvertFrom-Json).'builtin-baseline' + git -C "$env:VCPKG_INSTALLATION_ROOT" fetch --no-tags origin $baseline + if ($LASTEXITCODE -ne 0) { + git -C "$env:VCPKG_INSTALLATION_ROOT" fetch --tags --prune origin + } + git -C "$env:VCPKG_INSTALLATION_ROOT" cat-file -e "$baseline`^{commit}" + + - name: Configure CMake (Unix) + if: runner.os != 'Windows' + run: | + cmake -G Ninja -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" \ + -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} + + - name: Save vcpkg binary archives (Unix) + if: ${{ runner.os != 'Windows' && always() && steps.vcpkg_cache_restore_unix.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v4 + with: + path: ~/.cache/vcpkg/archives + key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} + + - name: Build (Unix) + if: runner.os != 'Windows' + run: cmake --build build --config Release + + - name: Configure CMake (Windows MSVC) + if: runner.os == 'Windows' && matrix.toolchain == 'msvc' + run: cmake -G Ninja -B build -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DRPS_MSVC_STATIC_RUNTIME=ON + + - name: Configure CMake (Windows Clang) + if: runner.os == 'Windows' && matrix.toolchain == 'clang' + run: cmake -G Ninja -B build -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg_triplet }} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DRPS_MSVC_STATIC_RUNTIME=ON + + - name: Save vcpkg binary archives (Windows) + if: ${{ runner.os == 'Windows' && always() && steps.vcpkg_cache_restore_windows.outputs.cache-hit != 'true' }} + uses: actions/cache/save@v4 + with: + path: ${{ steps.win_storage.outputs.vcpkg_cache_dir }} + key: vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} + + - name: Build (Windows) + if: runner.os == 'Windows' + run: cmake --build build --config Release + + - name: Stage & Archive Binaries (Unix) + if: runner.os != 'Windows' + run: | + mkdir -p release_stage + cp build/apps/rps-server/rps-server release_stage/ + cp build/apps/rps-standalone/rps-standalone release_stage/ + cp build/apps/rps-pluginscanner/rps-pluginscanner release_stage/ + cp build/apps/rps-vstscannermaster/vstscannermaster release_stage/ + cp build/examples/cpp/rps-example-client release_stage/ + cd release_stage + if [ "${{ matrix.archive_ext }}" = "tar.gz" ]; then + tar -czvf "../${{ matrix.artifact_name }}.tar.gz" ./* + else + zip -r "../${{ matrix.artifact_name }}.zip" ./* + fi + + - name: Stage & Zip Binaries (Windows MSVC) + if: runner.os == 'Windows' && matrix.toolchain == 'msvc' + run: | + New-Item -ItemType Directory -Force -Path release_stage + Copy-Item build\apps\rps-server\rps-server.exe release_stage\ + Copy-Item build\apps\rps-standalone\rps-standalone.exe release_stage\ + Copy-Item build\apps\rps-pluginscanner\rps-pluginscanner.exe release_stage\ + Copy-Item build\apps\rps-vstscannermaster\vstscannermaster.exe release_stage\ + Copy-Item build\examples\cpp\rps-example-client.exe release_stage\ + Compress-Archive -Path release_stage\* -DestinationPath "rps-binaries-${{ matrix.name }}.zip" + + - name: Stage & Archive Binaries (Windows Clang) + if: runner.os == 'Windows' && matrix.toolchain == 'clang' + run: | + New-Item -ItemType Directory -Force -Path release_stage + Copy-Item build\apps\rps-server\rps-server.exe release_stage\ + Copy-Item build\apps\rps-standalone\rps-standalone.exe release_stage\ + Copy-Item build\apps\rps-pluginscanner\rps-pluginscanner.exe release_stage\ + Copy-Item build\apps\rps-vstscannermaster\vstscannermaster.exe release_stage\ + Copy-Item build\examples\cpp\rps-example-client.exe release_stage\ + Compress-Archive -Path release_stage\* -DestinationPath "${{ matrix.artifact_name }}.zip" + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact_name }} + path: ${{ matrix.artifact_name }}.${{ matrix.archive_ext }} + retention-days: 7 From 403ea40c239bb178c04f8dabaf83fd04882ea27a Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 21:33:13 +0100 Subject: [PATCH 3/7] workflow fixes --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ef7e64..21253da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,15 +43,15 @@ jobs: # Windows - os: windows-latest - name: Windows Clang x64 + name: Windows x64 toolchain: clang artifact_name: rps-win-x64 archive_ext: zip vcpkg_triplet: x64-windows-static msvc_arch: x64 - - os: windows-latest - name: Windows Clang ARM64 + - os: windows-11-arm + name: Windows arm64 toolchain: clang artifact_name: rps-win-arm64 archive_ext: zip From 0149218f96dcb9545415a40472bc450459ee1d64 Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 21:41:34 +0100 Subject: [PATCH 4/7] test build fix. --- .github/workflows/build.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 21253da..320c4b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,12 +27,12 @@ jobs: vcpkg_triplet: arm64-linux # macOS - - os: macos-15-large - name: macOS Intel - toolchain: clang - artifact_name: rps-macos-intel - archive_ext: zip - vcpkg_triplet: x64-osx-release + # - os: macos-15-large + # name: macOS Intel + # toolchain: clang + # artifact_name: rps-macos-intel + # archive_ext: zip + # vcpkg_triplet: x64-osx-release - os: macos-15 name: macOS Apple Silicon @@ -169,17 +169,24 @@ jobs: restore-keys: | vcpkg-archives-${{ runner.os }}-${{ matrix.toolchain }}- - - name: Ensure vcpkg baseline commit is available (Unix) + - name: Ensure vcpkg registry and baseline commit are updated (Unix) if: runner.os != 'Windows' run: | + sudo git config --global --add safe.directory "$VCPKG_INSTALLATION_ROOT" + git -C "$VCPKG_INSTALLATION_ROOT" fetch origin master + git -C "$VCPKG_INSTALLATION_ROOT" checkout -f master + git -C "$VCPKG_INSTALLATION_ROOT" pull --rebase BASELINE=$(python3 -c "import json; print(json.load(open('vcpkg.json', encoding='utf-8'))['builtin-baseline'])") git -C "$VCPKG_INSTALLATION_ROOT" fetch --no-tags origin "$BASELINE" || git -C "$VCPKG_INSTALLATION_ROOT" fetch --tags --prune origin git -C "$VCPKG_INSTALLATION_ROOT" cat-file -e "${BASELINE}^{commit}" - - name: Ensure vcpkg baseline commit is available (Windows) + - name: Ensure vcpkg registry and baseline commit are updated (Windows) if: runner.os == 'Windows' shell: pwsh run: | + git -C "$env:VCPKG_INSTALLATION_ROOT" fetch origin master + git -C "$env:VCPKG_INSTALLATION_ROOT" checkout -f master + git -C "$env:VCPKG_INSTALLATION_ROOT" pull --rebase $baseline = (Get-Content vcpkg.json | ConvertFrom-Json).'builtin-baseline' git -C "$env:VCPKG_INSTALLATION_ROOT" fetch --no-tags origin $baseline if ($LASTEXITCODE -ne 0) { From e00c5d5a714480630b51776103abd7beae43e707 Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 21:48:54 +0100 Subject: [PATCH 5/7] windows arm ninja download --- .github/workflows/build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 320c4b2..09d04bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: artifact_name: rps-win-arm64 archive_ext: zip vcpkg_triplet: arm64-windows-static - msvc_arch: x64_arm64 + msvc_arch: arm64 steps: - name: Checkout repository @@ -75,6 +75,14 @@ jobs: run: | brew install cmake ninja pkg-config + - name: Install Build Tools (Windows ARM64) + if: runner.os == 'Windows' && matrix.msvc_arch == 'arm64' + shell: pwsh + run: | + Invoke-WebRequest -Uri "https://github.com/ninja-build/ninja/releases/download/v1.13.2/ninja-winarm64.zip" -OutFile "C:\ninja.zip" + Expand-Archive "C:\ninja.zip" -DestinationPath C:\ninja -Force + "C:\ninja" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: From 885b8ae73ec177e1c436a198cd52533019044709 Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 22:10:16 +0100 Subject: [PATCH 6/7] build fix --- .github/workflows/build.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 09d04bd..5560c5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,14 +75,6 @@ jobs: run: | brew install cmake ninja pkg-config - - name: Install Build Tools (Windows ARM64) - if: runner.os == 'Windows' && matrix.msvc_arch == 'arm64' - shell: pwsh - run: | - Invoke-WebRequest -Uri "https://github.com/ninja-build/ninja/releases/download/v1.13.2/ninja-winarm64.zip" -OutFile "C:\ninja.zip" - Expand-Archive "C:\ninja.zip" -DestinationPath C:\ninja -Force - "C:\ninja" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: @@ -157,6 +149,18 @@ jobs: Write-Host "Selected vcpkg storage root: $($best.Root)" Write-Host "Selected drive free space: $bestGb GB" + # --- VCPKG NINJA BOOTSTRAP FIX --- + # VCPKG on ARM64 struggles to extract ninja correctly due to 7zip architectural bugs. + # Pre-populating the exact directory VCPKG expects circumvents the broken inner downloader. + if ("${{ matrix.msvc_arch }}" -eq "arm64") { + $ninjaDir = Join-Path $downloads "tools\ninja-1.13.2-windows" + New-Item -ItemType Directory -Force -Path $ninjaDir | Out-Null + Invoke-WebRequest -Uri "https://github.com/ninja-build/ninja/releases/download/v1.13.2/ninja-winarm64.zip" -OutFile "$downloads\ninja.zip" + Expand-Archive "$downloads\ninja.zip" -DestinationPath $ninjaDir -Force + $ninjaDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + Write-Host "Bootstrapped ARM64 Ninja directly into $ninjaDir" + } + - name: Restore vcpkg binary archives (Unix) if: runner.os != 'Windows' id: vcpkg_cache_restore_unix From 4eceab9552c7321711a505c1bd142b90c2b3cf07 Mon Sep 17 00:00:00 2001 From: Benjamin Zeiss Date: Mon, 2 Mar 2026 22:37:29 +0100 Subject: [PATCH 7/7] max 2. --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5560c5a..01976a1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: fail-fast: false + max-parallel: 2 matrix: include: # Linux