diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 60448c8a13..2cc82d5a06 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -66,6 +66,23 @@ Set `CommunityShadersOutputDir` environment variable to semicolon-separated Skyr CommunityShadersOutputDir=F:/MySkyrimModpack/mods/CommunityShaders;F:/SteamLibrary/steamapps/common/SkyrimVR/Data;F:/SteamLibrary/steamapps/common/Skyrim Special Edition/Data ``` +### IDE / clangd setup + +The shipped presets use the Visual Studio generator, which does **not** emit a +`compile_commands.json`. Without one, clangd parses every TU with the fallback +flags in `.clangd` and reports false "unknown include / undefined symbol" +diagnostics. After configuring the `ALL` preset once, generate an accurate +compile database: + +```bash +pwsh tools/gen-clangd-db.ps1 +``` + +It stands up a throwaway Ninja configure (no build, reuses the `ALL` preset's +vcpkg packages), writes `compile_commands.json` to the repo root (gitignored, +machine-specific), and rewrites paths when the build runs under a `subst` drive. +Re-run it when the include graph or CMake options change, then restart clangd. + ### Shader Development and Testing ```bash @@ -561,6 +578,15 @@ After a hotfix release, open PRs targeting `dev` are auto-rebased by the `Auto-r Full details: [Open Shaders developer wiki — Patch Release Process](https://github.com/alandtse/open-shaders/wiki/Developers#patch-release-process-any-line). The fork now maintains its own wiki (transferred from upstream) at `alandtse/open-shaders/wiki`; the `maint-update-wiki.yaml` workflow auto-publishes buffer documentation there on every push to `dev`. Upstream Community Shaders maintains its own copy at `community-shaders/skyrim-community-shaders/wiki` — link to whichever is appropriate for the audience. +### Syncing from upstream Community Shaders + +Upstream is `community-shaders/skyrim-community-shaders`. Pull its changes **by merge, never cherry-pick** — this is the rule that keeps future syncs cheap: + +- **Merge, don't cherry-pick.** `git merge ` (an upstream `vX.Y.Z` tag — there is no `upstream/master`). Cherry-pick rewrites each commit to a new SHA, so upstream's originals never become ancestors and the **next** `git merge` re-conflicts on the same hunks forever. A merge makes the upstream commits ancestors once, so subsequent syncs are clean. +- **Land the sync PR as a merge commit, never squash.** Squash collapses the merge into one fresh SHA and throws the ancestry away, undoing the whole point. The repo default is squash, so the PR must be set to "Create a merge commit" explicitly (mirrors the #121 sync). +- **Verify ancestry after landing:** `git merge-base --is-ancestor HEAD` must pass for each adopted upstream commit. +- **Resolve conflicts in favor of keeping VR** (this fork is the VR maintainer). Typical conflicts are just fork CI config or feature `.ini` versions — resolve `--ours`. If upstream ships a VR removal, revert it and keep VR. + ### Code Organization and Refactoring Patterns - **Extract Large Functions**: Functions over ~200 lines should be broken into focused helper methods (see `FeatureListRenderer::DrawMenuVisitor` refactoring) diff --git a/.gitignore b/.gitignore index 6637671cd0..72497669e5 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ tests/shaders/*.exe tests/shaders/*.pdb tests/shaders/Shaders/ tools/__pycache__/ + +# clangd compile database (machine-specific absolute paths; regenerate with +# tools/gen-clangd-db.ps1) +/compile_commands.json diff --git a/tools/gen-clangd-db.ps1 b/tools/gen-clangd-db.ps1 new file mode 100644 index 0000000000..ead54f6c68 --- /dev/null +++ b/tools/gen-clangd-db.ps1 @@ -0,0 +1,121 @@ +#requires -Version 5.1 +<# +.SYNOPSIS + Generate a clangd compile_commands.json for this checkout. + +.DESCRIPTION + The shipped CMake presets use the Visual Studio generator, which does NOT emit + compile_commands.json (only Ninja/Makefile generators do). Without a real + compile DB, clangd parses every TU with the hand-maintained fallback flags in + .clangd and floods the editor with false "unknown include / undefined symbol" + diagnostics. This script stands up a throwaway Ninja configure (no build) purely + to emit an accurate DB, then drops it at the repo root where clangd + auto-discovers it. + + It reuses the vcpkg packages already installed by the -ReuseVcpkgFrom build dir + (default build/ALL), so it does not re-run a vcpkg install. Configure that + preset once before running this. + + MAX_PATH note: long checkout paths blow MAX_PATH for the FidelityFX/vcpkg + permutation headers, so some setups build under a `subst` drive. If a subst + drive maps to this checkout, the script builds under it and rewrites the + emitted DB paths back to the real checkout path (clangd opens files via the + real path, so the DB must match). With no subst drive it builds in-place. + + Re-run after any change that alters the include graph or defines (new source + file, new feature dir, CMake option flip). + +.EXAMPLE + pwsh tools/gen-clangd-db.ps1 +#> +[CmdletBinding()] +param( + # Build dir whose installed vcpkg packages are reused (so this does not + # re-run a full vcpkg install). Configure this preset first. + [string]$ReuseVcpkgFrom = 'build/ALL' +) +$ErrorActionPreference = 'Stop' + +$root = (& git -C $PSScriptRoot rev-parse --show-toplevel).Trim() +if (-not $root) { throw 'Not inside a git checkout' } +$rootFwd = ($root -replace '\\', '/').TrimEnd('/') + +# Build under a subst drive if one maps to this checkout (MAX_PATH workaround); +# otherwise build in-place at the repo root. +$drive = $null +foreach ($line in (subst)) { + # `subst` prints "Z:\: => C:\path"; tolerate the trailing colon and an + # optional backslash so the mapped-drive lookup actually matches. + if ($line -match '^([A-Z]):\\?:?\s*=>\s*(.+)$') { + if ($matches[2].TrimEnd('\') -ieq $root.TrimEnd('\')) { $drive = "$($matches[1]):"; break } + } +} +$base = if ($drive) { $drive } else { $rootFwd } +Write-Host "Checkout : $root" +Write-Host "Build at : $base$(if ($drive) { ' (subst drive; paths rewritten back)' })" + +# Discover the MSVC dev environment. Ninja needs cl on PATH; the VS generator +# sets this up implicitly, Ninja does not. -prerelease covers Insiders installs +# and is harmless on stable-only machines. +$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" +if (-not (Test-Path $vswhere)) { throw "vswhere.exe not found at $vswhere" } +$vsRoot = (& $vswhere -latest -prerelease -property installationPath | Select-Object -First 1).Trim() +if (-not $vsRoot) { throw 'vswhere found no Visual Studio install (need the Desktop development with C++ workload).' } +$vcvars = Join-Path $vsRoot 'VC\Auxiliary\Build\vcvars64.bat' +if (-not (Test-Path $vcvars)) { throw "vcvars64.bat not found under $vsRoot" } +cmd /c "`"$vcvars`" && set" | ForEach-Object { + if ($_ -match '^([^=]+)=(.*)$') { Set-Item -Path "env:$($matches[1])" -Value $matches[2] } +} +if (-not (Get-Command cl -ErrorAction SilentlyContinue)) { throw 'cl not on PATH after vcvars import' } + +# Read the vcpkg toolchain + triplet from the existing cache so we match the real +# build instead of hardcoding paths. +$cache = Join-Path $root "$ReuseVcpkgFrom/CMakeCache.txt" +if (-not (Test-Path $cache)) { throw "No CMakeCache at $cache - configure the $ReuseVcpkgFrom preset first" } +$cacheText = Get-Content $cache -Raw +$toolchain = ([regex]'CMAKE_TOOLCHAIN_FILE(?::\w+)?=(.+)').Match($cacheText).Groups[1].Value.Trim() +$triplet = ([regex]'VCPKG_TARGET_TRIPLET(?::\w+)?=(.+)').Match($cacheText).Groups[1].Value.Trim() +if (-not $toolchain) { throw "Could not read CMAKE_TOOLCHAIN_FILE from $cache" } +if (-not $triplet) { $triplet = 'x64-windows-static-md' } + +$genDir = "$base/build/clangd-gen" +$vcpkgDir = "$base/$ReuseVcpkgFrom/vcpkg_installed" + +# Configure-only (no build) under Ninja to emit the DB. /machine:x64 satisfies +# FidelityFX-SDK's platform check, which keys off the VS -A arch that Ninja +# doesn't set. Flags mirror the ALL preset (all three runtimes). +$cmakeArgs = @( + '-S', "$base/", '-B', $genDir, '-G', 'Ninja', + '-DCMAKE_BUILD_TYPE=Release', + '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON', + '-DCMAKE_EXE_LINKER_FLAGS=/machine:x64', + "-DCMAKE_TOOLCHAIN_FILE=$toolchain", + "-DVCPKG_OVERLAY_PORTS=$base/cmake/ports/", + "-DVCPKG_TARGET_TRIPLET=$triplet", + "-DVCPKG_INSTALLED_DIR=$vcpkgDir", + '-DVCPKG_MANIFEST_INSTALL=OFF', + '-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL', + '-DSKSE_SUPPORT_XBYAK=ON', + '-DENABLE_SKYRIM_AE=ON', '-DENABLE_SKYRIM_SE=ON', '-DENABLE_SKYRIM_VR=ON', + '-DAUTO_PLUGIN_DEPLOYMENT=OFF', + '-DCOMMONLIB_PREBUILT_MULTICONFIG=ON', + '-DBUILD_CPP_TESTS=OFF' +) +Write-Host 'Configuring Ninja DB (no build)...' +# Let cmake's diagnostics reach the console and check its native exit code — +# $ErrorActionPreference='Stop' does not trip on a non-zero exe exit, so a +# silent configure failure would otherwise surface only as the generic +# "did not produce compile_commands.json" below. +& cmake @cmakeArgs +if ($LASTEXITCODE -ne 0) { throw "cmake configure failed (exit code $LASTEXITCODE)." } +$dbSrc = Join-Path $root 'build/clangd-gen/compile_commands.json' +if (-not (Test-Path $dbSrc)) { throw 'Configure did not produce compile_commands.json' } + +# Drop the DB at repo root. When built under a subst drive, rewrite that drive +# root back to the real checkout path so clangd's file lookups match. +$text = [System.IO.File]::ReadAllText($dbSrc) +if ($drive) { $text = $text.Replace($drive, $rootFwd) } +$dest = Join-Path $root 'compile_commands.json' +[System.IO.File]::WriteAllText($dest, $text, (New-Object System.Text.UTF8Encoding($false))) +$count = ([regex]'"file":').Matches($text).Count +Write-Host "Wrote $dest ($count entries). Restart clangd to pick it up."