diff --git a/AUTHORING_EXTRACTOR_SKILL.md b/AUTHORING_EXTRACTOR_SKILL.md index 98412e6..d7bcc37 100644 --- a/AUTHORING_EXTRACTOR_SKILL.md +++ b/AUTHORING_EXTRACTOR_SKILL.md @@ -59,19 +59,28 @@ Point the extractor at a real `.sln`, `.slnx`, or `.vcxproj` file. ## Designing the SKILL A skill is a folder with a `SKILL.md`: YAML frontmatter (`name`, `description`) plus a body with -the pinned extraction command and any project lookup. Keep it generic and document only what is -different about your repo. The `description` is what makes the assistant auto-invoke the skill, -so phrase it with the words users type (generate, regenerate, or refresh compile commands; set -up C++ IntelliSense). +the extraction setup and any project lookup. Prefer a committed `msbuild-extractor.json` config file +and document only what is different about your repo. **Where the config lives is the user's choice.** +The extractor auto-detects `./msbuild-extractor.json` in the current directory, so a config at the +workspace root lets the skill run the exe with no arguments. But users are free to keep it elsewhere +(a subdirectory, a shared `build\` or `tools\` folder, a path outside the repo) and point the +extractor at it with `--config `; command-line flags take precedence over the config file. +Write the skill to accommodate both — document the auto-detected default, and note that a +non-standard location just means passing `--config`. The `description` is what makes the assistant +auto-invoke the skill, so phrase it with the words users type (generate, regenerate, or refresh +compile commands; set up C++ IntelliSense). ## What to put in your SKILL -These keep the generated database matched to your real build, so encode them in your `SKILL.md`. +These keep the generated database matched to your real build, so encode them in your `SKILL.md` — +preferably as keys in a committed `msbuild-extractor.json` (each flag below has a config-key +equivalent: `msBuildPath`, `vcTargetsPath`, `clPath`, `useDevEnv`, `configuration`, `platform`, +`msBuildProperties`), falling back to flags on the extraction command. -Bake into the extraction command: +Bake into the config (or extraction command): -- Use absolute paths for `--vc-targets-path` and `--cl-path`. -- Prefer out-of-process mode (`--msbuild-path`) in a vendored repo; when you have an activated developer environment (for example EWDK), `--use-dev-env` can also pin extraction to the intended toolchain. +- Use absolute paths for `--vc-targets-path`/`vcTargetsPath` and `--cl-path`/`clPath`. +- Prefer out-of-process mode (`--msbuild-path`/`msBuildPath`) in a vendored repo; when you have an activated developer environment (for example EWDK), `--use-dev-env`/`"useDevEnv": true` can also pin extraction to the intended toolchain. - Point at a `.sln` or a specific `.vcxproj`, not a generated or aggregate project file. Capture as prerequisites or notes in the skill body: @@ -128,8 +137,31 @@ activated EWDK shell and let the extractor read the toolchain from the environme ## Extraction command +Commit a `msbuild-extractor.json` so the extractor picks it up. Placing it at the repo root lets the +extractor auto-detect `./msbuild-extractor.json` and run with no arguments; if you prefer to keep it +elsewhere, point at it with `--config ` — it's your choice. For the EWDK, let it read the +activated toolchain from the environment with `useDevEnv`: + +```jsonc +{ + "solutions": ["general\\echo\\kmdf\\kmdfecho.sln"], + "configuration": "Debug", + "platform": "x64", + "useDevEnv": true, + "output": "compile_commands.json" +} +``` + From the activated EWDK shell, in the repo root: +```powershell +.tools\msbuild-extractor-sample.exe # uses ./msbuild-extractor.json +# or, if the config lives elsewhere: +.tools\msbuild-extractor-sample.exe --config path\to\msbuild-extractor.json +``` + +Equivalent one-off invocation with explicit flags (no committed config): + ```powershell .tools\msbuild-extractor-sample.exe ` --use-dev-env ` @@ -138,14 +170,28 @@ From the activated EWDK shell, in the repo root: -o compile_commands.json ``` -`--use-dev-env` reads the toolchain environment the EWDK exports (`VCToolsInstallDir`, +`--use-dev-env` / `"useDevEnv": true` reads the toolchain environment the EWDK exports (`VCToolsInstallDir`, `VCTargetsPath`, `WindowsSdkDir`, `INCLUDE`, `LIB`), which pins extraction to the EWDK. ## Alternative: explicit paths If you cannot use an activated shell, pin the EWDK tools by hand. On a machine that also has Visual Studio installed, prefer the activated shell so the extractor does not pick up the system -toolchain. +toolchain. As a committed config (`msbuild-extractor.json`): + +```jsonc +{ + "solutions": ["general\\echo\\kmdf\\kmdfecho.sln"], + "configuration": "Debug", + "platform": "x64", + "msBuildPath": "D:\\Program Files\\Microsoft Visual Studio\\18\\BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe", + "vcTargetsPath": "D:\\Program Files\\Microsoft Visual Studio\\18\\BuildTools\\MSBuild\\Microsoft\\VC\\v180", + "clPath": "D:\\Program Files\\Microsoft Visual Studio\\18\\BuildTools\\VC\\Tools\\MSVC\\14.50.35717\\bin\\Hostx64\\x64\\cl.exe", + "output": "compile_commands.json" +} +``` + +Or the equivalent one-off invocation: ```powershell .tools\msbuild-extractor-sample.exe ` diff --git a/plugins/cpp-language-server/skills/generate-compile-commands/SKILL.md b/plugins/cpp-language-server/skills/generate-compile-commands/SKILL.md index 5a8ad22..5f6f477 100644 --- a/plugins/cpp-language-server/skills/generate-compile-commands/SKILL.md +++ b/plugins/cpp-language-server/skills/generate-compile-commands/SKILL.md @@ -1,13 +1,13 @@ --- name: generate-compile-commands -description: Generate compile_commands.json (clang compilation database) for the C++ language server. Covers MSBuild (.sln, .slnx, .vcxproj) via microsoft/msbuild-extractor-sample and CMake projects via CMAKE_EXPORT_COMPILE_COMMANDS. Use whenever the user asks to "regenerate compile commands", "regenerate the project", "reload the project", "load the project", "refresh compile commands", or otherwise needs a fresh compilation database for mscppls. +description: Generate compile_commands.json (clang compilation database) for the C++ language server. Covers MSBuild (.sln, .slnx, .vcxproj) via microsoft/msbuild-extractor-sample — driven by a committed msbuild-extractor.json config file — and CMake projects via CMAKE_EXPORT_COMPILE_COMMANDS. Use whenever the user asks to "regenerate compile commands", "regenerate the project", "reload the project", "load the project", "refresh compile commands", set up an msbuild-extractor.json / extractor config, or otherwise needs a fresh compilation database for mscppls. --- # Generate compile_commands.json The C++ language server needs a `compile_commands.json` (clang compilation database) to understand a project. How you produce one depends on the build system: CMake can export it directly, MSBuild needs an external extractor. -Always regenerate `compile_commands.json` when this skill runs, even if one already exists for the project. For MSBuild, write it to `.mscppls\compile_commands.json` at the workspace root (the examples below do this); mscppls auto-discovers any `compile_commands.json` within the workspace, so no `cpp-lsp.json` is required. Add `.mscppls/` to `.gitignore` so it does not get committed. mscppls hot-reloads the file content; `/restart` is only needed after changing the plugin's `lsp.json` or the workspace `cpp-lsp.json` (those are read once at LSP startup). +Always regenerate `compile_commands.json` when this skill runs, even if one already exists for the project. For MSBuild, write it to `.mscppls\compile_commands.json` at the workspace root (the examples below do this); mscppls auto-discovers any `compile_commands.json` within the workspace, so no `cpp-lsp.json` is required. Add `.mscppls/` to `.gitignore` so the generated database is not committed — but do **commit** the `msbuild-extractor.json` config file (see below) so the whole team regenerates identically. mscppls hot-reloads the file content; `/restart` is only needed after changing the plugin's `lsp.json` or the workspace `cpp-lsp.json` (those are read once at LSP startup). ## CMake projects @@ -45,8 +45,8 @@ For MSBuild (`.sln`, `.slnx`, `.vcxproj`), generate `compile_commands.json` with Download the pinned release and verify its SHA256 before trusting it. The binary is self-contained, with no .NET runtime needed. ```powershell -$version = 'v0.2.0' -$expected = '01a5e2d399c3cb84d3136d0b6ef7b84a05900838ddc3b1f710b9394c8c2936ce' +$version = 'v0.3.0' +$expected = '543c5cc6b57a1b3eb46b11e56b8f35a9ca8676106426bde6041ca2dc2e06f13c' $exe = ".tools\msbuild-extractor-sample.exe" if (-not (Test-Path $exe) -or (Get-FileHash $exe -Algorithm SHA256).Hash -ne $expected.ToUpper()) { @@ -64,7 +64,81 @@ if (-not (Test-Path $exe) -or (Get-FileHash $exe -Algorithm SHA256).Hash -ne $ex When upgrading the pinned version, get the new SHA256 from the `digest` field of the asset at `https://api.github.com/repos/microsoft/msbuild-extractor-sample/releases/latest`. -### Run it +### Recommended: a committed `msbuild-extractor.json` + +**This is the primary MSBuild workflow.** Instead of memorizing extractor flags and re-passing them on every run, commit a single `msbuild-extractor.json` at the workspace root that captures the project inputs and toolchain settings. Everyone (and every agent run) then regenerates the database identically. + +The extractor auto-discovers this file: when `--config` is not passed, it loads `msbuild-extractor.json` from the current directory if present. So a committed config reduces the whole invocation to running the exe with no arguments. + +**Decision path:** if a `msbuild-extractor.json` already exists, run the extractor with no flags (below). If it does **not** exist, fall back to the raw CLI flags to get a working run, then write out a `msbuild-extractor.json` capturing those settings so it can be committed (see [Fallback: raw CLI flags](#fallback-raw-cli-flags)). + +- **Auto-detection:** `msbuild-extractor.json` in the current directory is used automatically; pass `--config ` only if it lives elsewhere. +- **Precedence:** command-line flags override the config file, which overrides built-in defaults. Use flags for one-off overrides, the config for the committed baseline. +- **Relative paths** in the config resolve from the config file's own directory (so a workspace-root config can reference `src\app\app.vcxproj`, `.mscppls\compile_commands.json`, etc.). +- At least one `projects` or `solutions` entry is required (in the config or on the command line). + +Author (or update) `msbuild-extractor.json` at the workspace root and commit it. Point `output` at `.mscppls\compile_commands.json` so it lands where mscppls auto-discovers it: + +```jsonc +{ + // Inputs: at least one solutions[] or projects[] entry is required. + "solutions": ["myapp.sln"], + "configuration": "Debug", + "platform": "x64", + "output": ".mscppls\\compile_commands.json", + + // Optional but recommended for IntelliSense across configs: + // "allConfigurations": true, + // "merge": true, + // "deduplicate": true +} +``` + +Then generate — no flags needed, the config is auto-detected: + +```powershell +New-Item -ItemType Directory -Path .mscppls -Force | Out-Null +.tools\msbuild-extractor-sample.exe # uses ./msbuild-extractor.json +# or, if the config lives elsewhere: +.tools\msbuild-extractor-sample.exe --config path\to\msbuild-extractor.json +``` + +Remember: commit `msbuild-extractor.json`, but keep `.mscppls/` in `.gitignore` (the generated database is not committed). + +#### CLI flag → config key reference + +Every flag in the fallback sections below has a config-file equivalent. Set the committed baseline as keys; reach for the flag only to override for a single run. + +| Concern | CLI flag(s) | Config key | +|---|---|---| +| Inputs | `--project`, `--solution` (repeatable) | `projects: []`, `solutions: []` | +| Configuration / platform | `-c`/`--configuration`, `-a`/`--platform` | `configuration`, `platform` | +| Output / format | `-o`/`--output`, `-f`/`--format` | `output`, `format` (`"standard"`/`"rich"`) | +| Multi-config | `--all-configurations`, `--merge`, `--deduplicate`, `--prefer-configuration`, `--prefer-platform` | `allConfigurations`, `merge`, `deduplicate`, `preferConfiguration`, `preferPlatform` | +| Dev-env toolchain | `--use-dev-env` | `useDevEnv` | +| VS selection | `--vs-instance`, `--vs-path` | `vsInstance`, `vsPath` | +| Vendored / out-of-process toolchain | `--msbuild-path`, `--vc-targets-path`, `--cl-path`, `--vc-tools-install-dir`, `--solution-dir` | `msBuildPath`, `vcTargetsPath`, `clPath`, `vcToolsInstallDir`, `solutionDir` | +| MSBuild launch / includes | `--msbuild-launcher`, `--include-path-order` | `msBuildLauncher`, `includePathOrder` | +| MSBuild properties / env | `--msbuild-property KEY=VALUE`, `--msbuild-env KEY=VALUE` (repeatable) | `msBuildProperties: {}`, `msBuildEnv: {}` (objects) | + +`msBuildProperties` and `msBuildEnv` are objects that **merge** with their CLI counterparts (`--msbuild-property` / `--msbuild-env`), with the CLI winning on key collisions — so a committed baseline can be topped up on the command line for a single run. + +### Fallback: raw CLI flags + +Use these when you don't want a committed config (a one-off run) or need to override the config for a single invocation. Each example below has a config-key equivalent per the table above; prefer moving durable settings into the committed `msbuild-extractor.json`. + +**When no `msbuild-extractor.json` exists yet, use the CLI to get a working run — then persist it.** After the extractor succeeds with a given set of flags, write those exact settings out as a `msbuild-extractor.json` at the workspace root (translating each flag to its config key via the table above) and tell the user to commit it. Subsequent runs then need no flags. For example, after a successful `--solution myapp.sln -c Debug -a x64 -o .mscppls\compile_commands.json`, create: + +```jsonc +{ + "solutions": ["myapp.sln"], + "configuration": "Debug", + "platform": "x64", + "output": ".mscppls\\compile_commands.json" +} +``` + +Only persist flags that succeeded, and omit machine-specific absolute paths that won't be valid on a teammate's checkout (prefer relative paths, which resolve from the config's directory; for an activated hermetic toolchain prefer `"useDevEnv": true` over absolute `msBuildPath`/`clPath`). Don't overwrite an existing `msbuild-extractor.json` without confirming with the user. For a typical project on a workstation with Visual Studio installed, the extractor auto-detects Visual Studio via `vswhere.exe` and resolves `VCTargetsPath`, `VCToolsInstallDir`, and `cl.exe` automatically. Solution-based extraction is the simplest entry point: @@ -76,7 +150,7 @@ New-Item -ItemType Directory -Path .mscppls -Force | Out-Null -o .mscppls\compile_commands.json ``` -`-c` is `--configuration` (default `Debug`), `-a` is `--platform` (e.g. `x64`, `Win32`, `ARM64`), `-o` is `--output`. `--solution` accepts `.sln` and `.slnx`; it is repeatable for cross-solution work. `--project` works the same way for individual `.vcxproj` files (also repeatable, deduplicates entries). +`-c` is `--configuration` (default `Debug`), `-a` is `--platform` (e.g. `x64`, `Win32`, `ARM64`), `-o` is `--output`. `--solution` accepts `.sln` and `.slnx`; it is repeatable for cross-solution work. `--project` works the same way for individual `.vcxproj` files (also repeatable, deduplicates entries). Config equivalents: `solutions`/`projects`, `configuration`, `platform`, `output`. #### From a Developer Command Prompt or activated vcvars shell @@ -90,7 +164,7 @@ If you launched a Developer Command Prompt / Developer PowerShell for VS, or ran -o .mscppls\compile_commands.json ``` -This guarantees the LSP indexes against the exact same toolchain the developer is building with. Use it whenever the project depends on a specific VS version, SDK, or side-by-side toolchain that the default `vswhere` heuristic might not pick. +This guarantees the LSP indexes against the exact same toolchain the developer is building with. Use it whenever the project depends on a specific VS version, SDK, or side-by-side toolchain that the default `vswhere` heuristic might not pick. Config equivalent: `"useDevEnv": true`. #### Multiple VS installs @@ -101,6 +175,8 @@ If `vswhere` picks the wrong one, list and select explicitly: .tools\msbuild-extractor-sample.exe --vs-instance --solution myapp.sln -c Debug -a x64 ``` +Config equivalent: pin `"vsInstance": ""` (or `"vsPath"`) in the committed config so every run selects the same install. + #### Multi-configuration projects Prefer the built-in flags over manual merging; they produce one IntelliSense-friendly entry per source file across all configurations: @@ -112,6 +188,8 @@ Prefer the built-in flags over manual merging; they produce one IntelliSense-fri -o .mscppls\compile_commands.json ``` +Config equivalent: `"allConfigurations": true`, `"merge": true`, `"deduplicate": true` (see the recommended example above). + Skip `--validate` during normal LSP setup. It compiles every TU with `cl.exe /c` and is slow. Reach for it only when debugging the extractor itself. ### Advanced: out-of-process mode for vendored or hermetic-toolchain repos @@ -148,6 +226,20 @@ Example (the Windows driver samples built with the Enterprise WDK; the version f When the toolchain is already activated in the current shell (for example after running the EWDK's `LaunchBuildEnv.cmd`), pass `--use-dev-env` instead of the explicit `--msbuild-path`, `--vc-targets-path`, and `--cl-path`, and the extractor reads those paths from the environment. +Config equivalent: these are exactly the settings to **commit** so every checkout resolves the same toolchain. Because the EWDK paths above are absolute and machine-specific, the portable committed form is to activate the toolchain first and set `"useDevEnv": true` in `msbuild-extractor.json`: + +```jsonc +{ + "solutions": ["general\\echo\\kmdf\\kmdfecho.sln"], + "configuration": "Debug", + "platform": "x64", + "useDevEnv": true, + "output": ".mscppls\\compile_commands.json" +} +``` + +If a repo genuinely pins fixed toolchain paths, set `msBuildPath`, `vcTargetsPath`, `clPath`, and any `msBuildProperties` in the config instead — but keep them relative where possible so teammates' checkouts resolve them. + The extractor cannot consume `dirs.proj` or aggregate `.proj` files; point it at a real leaf `.vcxproj` (or `.sln`). If the repo needs initialization for its props, targets, and dependencies to resolve, initialize it once, then run the extractor. @@ -170,7 +262,7 @@ Get-ChildItem .mscppls\compile_commands_*.json | ForEach-Object { ConvertTo-Json -InputObject @($all.Values) -Depth 6 | Set-Content .mscppls\compile_commands.json -Encoding UTF8 ``` -`@($all.Values)` forces an array even with a single entry; otherwise `ConvertTo-Json` emits a bare object and the file is not a valid compilation database. +`@($all.Values)` forces an array even with a single entry; otherwise `ConvertTo-Json` emits a bare object and the file is not a valid compilation database. Because these invocations use conflicting flags they can't be captured by a single config file; keep the shared settings in `msbuild-extractor.json` and pass only the conflicting `--configuration` / `--platform` / `--msbuild-property` overrides on each command line. ## Troubleshooting @@ -178,10 +270,10 @@ These cover the MSBuild extractor path. For CMake, build-system errors come from | Symptom | Cause | Fix | |---|---|---| -| `REGDB_E_INVALIDVALUE`, or compile commands point at the wrong `cl.exe` | In-proc MSBuild picked up a system VS install instead of the repo's vendored toolchain | Switch to out-of-process mode with `--msbuild-path` and pin `--vc-targets-path` / `--cl-path` | -| Extractor hangs | Transitive project-reference evaluation on a deep build graph | Add `--msbuild-property BuildProjectReferences=false` | +| `REGDB_E_INVALIDVALUE`, or compile commands point at the wrong `cl.exe` | In-proc MSBuild picked up a system VS install instead of the repo's vendored toolchain | Switch to out-of-process mode: set `msBuildPath` and pin `vcTargetsPath` / `clPath` in the committed `msbuild-extractor.json` (or pass `--msbuild-path` / `--vc-targets-path` / `--cl-path`) | +| Extractor hangs | Transitive project-reference evaluation on a deep build graph | Set `"msBuildProperties": { "BuildProjectReferences": "false" }` in the config (or `--msbuild-property BuildProjectReferences=false`) | | `--validate` reports missing module `.ifc` | Module producer not built | Build dependencies first, or just skip `--validate` | | LSP doesn't see newly extracted entries | mscppls observer is still hashing the new file | Wait a few seconds; mscppls hot-reloads, so do **not** `/restart` | -| `vswhere` picks the wrong VS install | Multiple VS instances present | `--list-instances` then `--vs-instance ` | +| `vswhere` picks the wrong VS install | Multiple VS instances present | `--list-instances`, then pin `"vsInstance": ""` in the config (or `--vs-instance `) | For extractor diagnostics, re-run the same command and read its stderr inline; don't redirect to a file. diff --git a/skills/setup-cpp-language-server/SKILL.md b/skills/setup-cpp-language-server/SKILL.md index 6cff6b2..ee26a69 100644 --- a/skills/setup-cpp-language-server/SKILL.md +++ b/skills/setup-cpp-language-server/SKILL.md @@ -45,7 +45,7 @@ Your job is to ensure these configuration files are correctly set up. 1. If all required files exist and appear correct, stop. All checks have passed and it's okay to run the C++ LSP tools. Note that your built-in search tools often ignore files in the build directory, so double-check using terminal commands. 2. If `compile_commands.json` does not exist, help the user create the file for their project. Don't complete any other tasks and ensure the `compile_commands.json` file is created before continuing. a. For CMake projects, it's often possible to produce the file by including `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`. This option is not supported when using the Visual Studio generators (default for CMake on Windows), but it is supported with Ninja and Makefile generators. - b. For MSBuild-based C++ projects (`.vcxproj`, `.sln`, `.slnx`), use [`msbuild-extractor-sample`](https://github.com/microsoft/msbuild-extractor-sample/releases) to generate the file, e.g. `msbuild-extractor-sample.exe --solution path\to\app.sln -c Debug -a x64 -o compile_commands.json`. + b. For MSBuild-based C++ projects (`.vcxproj`, `.sln`, `.slnx`), use [`msbuild-extractor-sample`](https://github.com/microsoft/msbuild-extractor-sample/releases) to generate the file. Prefer committing a `msbuild-extractor.json` config file at the workspace root so the tool runs with no arguments (`msbuild-extractor-sample.exe`, which auto-detects `./msbuild-extractor.json`); otherwise pass flags directly, e.g. `msbuild-extractor-sample.exe --solution path\to\app.sln -c Debug -a x64 -o compile_commands.json`. 3. If `.mscppls/cpp-lsp.json` does not exist, create it based on the path to the `compile_commands.json` file. 4. If `.github/lsp.json` does not exist or does not have an entry for C++, create or update it to include the configuration shown above. Make sure the `args` field points to the correct path to `cpp-lsp.json`. 5. If you made any changes to `compile_commands.json`, `.mscppls/cpp-lsp.json`, or `.github/lsp.json`, guide the user to restart their chat session before using the C++ LSP tools. The tools need to be restarted. \ No newline at end of file