Skip to main content

presets-vs-kits

Use when writing or reviewing code that must work in both CMake Presets and Kits/Variants modes. Covers configure, build, test, environment, and generator paths. Triggers: "presets vs kits", "useCMakePresets", "works in both modes".

소스 정보

저장소
microsoft/vscode-cmake-tools
최근 소스 활동
2026년 4월 20일 20:29
감지된 SKILL.md 언어
영어
스타
1,686
포크
551

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
presets-vs-kits
description
Use when writing or reviewing code that must work in both CMake Presets and Kits/Variants modes. Covers configure, build, test, environment, and generator paths. Triggers: "presets vs kits", "useCMakePresets", "works in both modes".
# Presets Mode vs Kits/Variants Mode 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. --- ## The two 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` | ### How the mode is determined ```typescript // 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. --- ## When you MUST handle both modes **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. ### The canonical branching pattern ```typescript // 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 }; } ``` ### Another real example — default build targets ```typescript // src/cmakeProject.ts — getDefaultBuildTargets() if (this.useCMakePresets && (!defaultTarget || defaultTarget === this.targetsInPresetName)) { targets = this.buildPreset?.targets; } if (!this.useCMakePresets && !defaultTarget) { targets = await this.allTargetName; } ``` --- ## Canonical data access — Presets mode ### Merged preset tree 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. ### Active presets on `CMakeProject` ```typescript CMakeProject.configurePreset // ConfigurePreset | null CMakeProject.buildPreset // BuildPreset | null CMakeProject.testPreset // TestPreset | null CMakeProject.packagePreset // PackagePreset | null CMakeProject.workflowPreset // WorkflowPreset | null ``` ### Preset types All preset interfaces are defined in `src/presets/preset.ts`. Key types include `ConfigurePreset`, `BuildPreset`, `TestPreset`, `PackagePreset`, `WorkflowPreset`. ### CMake executable override In presets mode, the configure preset can override the CMake path: ```typescript // src/cmakeProject.ts const overWriteCMakePathSetting = this.useCMakePresets ? this.configurePreset?.cmakeExecutable : undefined; ``` --- ## Canonical data access — Kits/variants mode ### Active kit ```typescript CMakeProject.activeKit // Kit | null — from src/kits/kit.ts ``` ### Kit environment ```typescript 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. ### SpecialKits — always check before treating a kit as a real compiler ```typescript // 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: ```typescript if (kit.name === SpecialKits.Unspecified || kit.name === SpecialKits.ScanForKits) { // Not a real kit — skip compiler logic } ``` ### Kit interface ```typescript // 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; } ``` ### Variant handling 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: ```typescript // src/cmakeProject.ts if (!this.useCMakePresets) { await this.variantManager.initialize(this.folderName); await drv.setVariant(this.variantManager.activeVariantOptions, ...); } ``` --- ## Generator handling in both modes ### Multi-config vs single-config Always check the generator before any build-type logic: ```typescript 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 | ### Exception: `cmake.setBuildTypeOnMultiConfig` When 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`. ### How it shows up in both modes - **Presets mode**: The generator is embedded in `configurePreset.generator`. The preset may also set `CMAKE_BUILD_TYPE` in `cacheVariables` or use `configuration` in the build preset. - **Kits mode**: The generator comes from `Kit.preferredGenerator.name` or is auto-detected. Build type comes from the active variant. --- ## Common mistakes ### 1. Testing only one mode 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). ### 2. Assuming single-config generator Never assume `CMAKE_BUILD_TYPE` is the way to set the build configuration. Always check `isMultiConfGeneratorFast()` first. ```typescript // ❌ Wrong — breaks with Visual Studio / Ninja Multi-Config args.push(`-DCMAKE_BUILD_TYPE=${buildType}`); // ✅ Correct if (!isMultiConfGeneratorFast(generator)) { args.push(`-DCMAKE_BUILD_TYPE=${buildType}`); } ``` ### 3. Reading preset files directly ```typescript // ❌ Wrong — bypasses include chaining, expansion, and caching const presets = JSON.parse(fs.readFileSync('CMakePresets.json', 'utf8')); // ✅ Correct — use the PresetsController const configPreset = this.configurePreset; ``` ### 4. Not checking for SpecialKits sentinel values ```typescript // ❌ 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']; } ``` ### 5. Forgetting the driver instance guard In kits mode, no driver is created without a kit: ```typescript // 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. --- ## Testing checklist - [ ] Verified in **presets mode** (create a `CMakePresets.json` with configure + build presets) - [ ] Verified in **kits/variants mode** (remove preset files or set `cmake.useCMakePresets: "never"`) - [ ] Verified with a **single-config** generator (Ninja or Unix Makefiles) - [ ] Verified with a **multi-config** generator (Visual Studio, Ninja Multi-Config, or Xcode) - [ ] `SpecialKits` sentinel values handled (no crash on `__unspec__` kit) - [ ] `null` driver / `null` preset / `null` kit cases handled gracefully - [ ] Windows, macOS, and Linux path differences considered --- See also: [`.github/copilot-instructions.md`](../copilot-instructions.md) for project-wide conventions.
GitHub에서 보기