> Use when writing or reviewing code that must work in both CMake Presets and Kits/Variants modes. Covers configure, build, test, environment, and generator
npx skills add https://github.com/microsoft/vscode-cmake-tools --skill presets-vs-kits
Guide for writing code that works correctly in both operating modes of CMake Tools. Any shared code path (configure, build, test, environment, targets) must handle both modes.
CMake Tools operates in one of two mutually exclusive modes per project folder:
| | Presets mode | Kits/variants mode |
|---|---|---|
| Source of truth | CMakePresets.json / CMakeUserPresets.json | Kit selection + variant settings |
| When active | cmake.useCMakePresets is 'always', or 'auto' and preset files exist | cmake.useCMakePresets is 'never', or 'auto' and no preset files exist |
| Runtime check | CMakeProject.useCMakePresets === true | CMakeProject.useCMakePresets === false |
// src/config.ts
export type UseCMakePresets = 'always' | 'never' | 'auto';
// src/cmakeProject.ts — doUseCMakePresetsChange()
const usingCMakePresets =
useCMakePresets === 'always' ? true :
useCMakePresets === 'never' ? false :
await this.hasPresetsFiles(); // 'auto' — check for CMakePresets.json
The resolved boolean is stored in CMakeProject._useCMakePresets and exposed via the useCMakePresets getter.
Any shared code path that touches configure, build, test, environment setup, or target resolution must branch on the mode. Omitting the check for one mode is a latent bug.
// From src/cmakeProject.ts — configure validation (real code)
if (!this.useCMakePresets) {
// Kits mode: require a kit and variant
if (!this.activeKit) {
await vscode.window.showErrorMessage(
localize('cannot.configure.no.kit', 'Cannot configure: No kit is active for this CMake project')
);
return { exitCode: -1, resultType: ConfigureResultType.Other };
}
if (!this.variantManager.haveVariant) {
await this.variantManager.selectVariant();
}
} else if (!this.configurePreset) {
// Presets mode: require a configure preset
void vscode.window.showErrorMessage(
localize('cannot.configure.no.config.preset', 'Cannot configure: No configure preset is active')
);
return { exitCode: -1, resultType: ConfigureResultType.Other };
}
// src/cmakeProject.ts — getDefaultBuildTargets()
if (this.useCMakePresets && (!defaultTarget || defaultTarget === this.targetsInPresetName)) {
targets = this.buildPreset?.targets;
}
if (!this.useCMakePresets && !defaultTarget) {
targets = await this.allTargetName;
}
Use PresetsController (in src/presets/presetsController.ts) to access the merged, expanded preset tree. Never re-parse CMakePresets.json or CMakeUserPresets.json directly — the controller handles include chaining, expansion, and file watching.
CMakeProjectCMakeProject.configurePreset // ConfigurePreset | null
CMakeProject.buildPreset // BuildPreset | null
CMakeProject.testPreset // TestPreset | null
CMakeProject.packagePreset // PackagePreset | null
CMakeProject.workflowPreset // WorkflowPreset | null
All preset interfaces are defined in src/presets/preset.ts. Key types include ConfigurePreset, BuildPreset, TestPreset, PackagePreset, WorkflowPreset.
In presets mode, the configure preset can override the CMake path:
// src/cmakeProject.ts
const overWriteCMakePathSetting = this.useCMakePresets
? this.configurePreset?.cmakeExecutable
: undefined;
CMakeProject.activeKit // Kit | null — from src/kits/kit.ts
import { effectiveKitEnvironment } from '@cmt/kits/kit';
// Used in src/drivers/cmakeDriver.ts:
this._kitEnvironmentVariables = await effectiveKitEnvironment(kit, this.expansionOptions);
This merges compiler paths, VS Developer Environment (on Windows via vcvarsall.bat), and any custom environmentVariables defined on the kit.
// src/kits/kit.ts
export enum SpecialKits {
ScanForKits = '__scanforkits__',
Unspecified = '__unspec__',
ScanSpecificDir = '__scan_specific_dir__',
}
These sentinel values appear in the kit list as UI actions. Before using kit.compilers, kit.toolchainFile, or kit.preferredGenerator, check:
if (kit.name === SpecialKits.Unspecified || kit.name === SpecialKits.ScanForKits) {
// Not a real kit — skip compiler logic
}
// src/kits/kit.ts (key fields)
export interface Kit extends KitDetect {
name: string;
description?: string;
preferredGenerator?: CMakeGenerator;
cmakeSettings?: Record<string, string | string[]>;
compilers?: Record<string, string>;
toolchainFile?: string;
environmentVariables?: Record<string, string>;
environmentSetupScript?: string;
visualStudio?: string;
visualStudioArchitecture?: string;
}
Variants (src/kits/variant.ts) provide build type and other CMake variable overrides in kits mode. The VariantManager is only initialized when not using presets:
// src/cmakeProject.ts
if (!this.useCMakePresets) {
await this.variantManager.initialize(this.folderName);
await drv.setVariant(this.variantManager.activeVariantOptions, ...);
}
Always check the generator before any build-type logic:
import { isMultiConfGeneratorFast } from '@cmt/util';
// src/util.ts
export function isMultiConfGeneratorFast(gen?: string): boolean {
return gen !== undefined
&& (gen.includes('Visual Studio') || gen.includes('Xcode') || gen.includes('Multi-Config'));
}
| Generator type | Examples | Build type mechanism |
|---|---|---|
| Single-config | Ninja, Unix Makefiles | CMAKE_BUILD_TYPE set at configure time |
| Multi-config | Visual Studio, Xcode, Ninja Multi-Config | --config <type> passed at build time |
cmake.setBuildTypeOnMultiConfigWhen this setting is true, CMAKE_BUILD_TYPE is also set at configure time for multi-config generators (used by some CMake scripts that read it). See src/drivers/cmakeDriver.ts.
configurePreset.generator. The preset may also set CMAKE_BUILD_TYPE in cacheVariables or use configuration in the build preset.Kit.preferredGenerator.name or is auto-detected. Build type comes from the active variant.If you add or change behavior in a shared code path, you must verify it works in both presets mode and kits/variants mode. Many bugs ship because the developer only tested with presets (or only with kits).
Never assume CMAKE_BUILD_TYPE is the way to set the build configuration. Always check isMultiConfGeneratorFast() first.
// ❌ Wrong — breaks with Visual Studio / Ninja Multi-Config
args.push(`-DCMAKE_BUILD_TYPE=${buildType}`);
// ✅ Correct
if (!isMultiConfGeneratorFast(generator)) {
args.push(`-DCMAKE_BUILD_TYPE=${buildType}`);
}
// ❌ Wrong — bypasses include chaining, expansion, and caching
const presets = JSON.parse(fs.readFileSync('CMakePresets.json', 'utf8'));
// ✅ Correct — use the PresetsController
const configPreset = this.configurePreset;
// ❌ Wrong — crashes when kit is '__unspec__' or '__scanforkits__'
const compiler = kit.compilers?.['C'];
// ✅ Correct
if (kit.name !== SpecialKits.Unspecified && kit.name !== SpecialKits.ScanForKits) {
const compiler = kit.compilers?.['C'];
}
In kits mode, no driver is created without a kit:
// src/cmakeProject.ts
if (!this.useCMakePresets && !this.activeKit) {
log.debug(localize('not.starting.no.kits', 'Not starting CMake driver: no kit selected'));
return null;
}
Always handle null driver returns in calling code.
CMakePresets.json with configure + build presets)cmake.useCMakePresets: "never")SpecialKits sentinel values handled (no crash on __unspec__ kit)null driver / null preset / null kit cases handled gracefullySee also: .github/copilot-instructions.md for project-wide conventions.
Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
Structured task planning with clear breakdowns, dependencies, and verification criteria. Use when implementing features, refactoring, or any multi-step work.
Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
Guidelines and format for writing pull request descriptions in this repository. Use this skill whenever the user asks you to draft a pull request description, submit a PR, or update a PR description.
Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency.
Use when a user asks to debug or fix failing GitHub PR checks that run in GitHub Actions. Uses `gh` to inspect checks and logs, summarize failure context, draft a fix plan, and implement only after explicit approval. Treats external providers (for example Buildkite) as out of scope and reports only the details URL. Do NOT use for addressing PR review comments (use gh-address-comments) or general CI outside GitHub Actions.
Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency.
Take microsoft/presets-vs-kits from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.