From 8df6da2656c268bff4d5cc8e9bcecc153ef5ddfd Mon Sep 17 00:00:00 2001 From: Hannia Valera Date: Thu, 16 Apr 2026 12:24:48 -0500 Subject: [PATCH 1/5] Enhance generator mismatch detection and clean-up logic - Added a new method to clean prior configurations if the generator changes.. - Removed deprecated functions related to Visual Studio generator mapping. - Introduced unit tests to verify generator mismatch detection and kit change logic. --- CHANGELOG.md | 5 + src/drivers/cmakeDriver.ts | 35 ++++-- src/kits/kit.ts | 54 --------- test/unit-tests/generator-mismatch.test.ts | 101 ++++++++++++++++ test/unit-tests/kit-scan.test.ts | 131 +++++++++++++++++++-- 5 files changed, 249 insertions(+), 77 deletions(-) create mode 100644 test/unit-tests/generator-mismatch.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 868cf7e72..eb470078a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ Bug Fixes: - Fix kit detection returning "unknown vendor" when using clang-cl compiler. [#4638](https://github.com/microsoft/vscode-cmake-tools/issues/4638) - Update testing framework to fix bugs when running tests of CMake Tools without a reliable internet connection. [#4891](https://github.com/microsoft/vscode-cmake-tools/pull/4891) [@cwalther](https://github.com/cwalther) +## 1.23.51 + +Bug Fixes: +- Fix regression where VS kits with existing Ninja cache would fail due to generator mismatch after update. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) + ## 1.23 Features: diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts index cd8f6f933..da5e4ffbb 100644 --- a/src/drivers/cmakeDriver.ts +++ b/src/drivers/cmakeDriver.ts @@ -16,7 +16,7 @@ import { CMakeOutputConsumer } from '@cmt/diagnostics/cmake'; import { RawDiagnosticParser } from '@cmt/diagnostics/util'; import { ProgressMessage } from '@cmt/drivers/drivers'; import * as expand from '@cmt/expand'; -import { CMakeGenerator, effectiveKitEnvironment, Kit, kitChangeNeedsClean, KitDetect, getKitDetect, getVSKitEnvironment, getVsKitPreferredGenerator } from '@cmt/kits/kit'; +import { CMakeGenerator, effectiveKitEnvironment, Kit, kitChangeNeedsClean, KitDetect, getKitDetect, getVSKitEnvironment } from '@cmt/kits/kit'; import * as logging from '@cmt/logging'; import paths from '@cmt/paths'; import { fs } from '@cmt/pr'; @@ -32,7 +32,7 @@ import * as codeModel from '@cmt/drivers/codeModel'; import { Environment, EnvironmentUtils } from '@cmt/environmentVariables'; import { CMakeTask, CMakeTaskProvider, CustomBuildTaskTerminal } from '@cmt/cmakeTaskProvider'; import { getValue } from '@cmt/presets/preset'; -import { CacheEntry } from '@cmt/cache'; +import { CacheEntry, CMakeCache } from '@cmt/cache'; import { CMakeBuildRunner } from '@cmt/cmakeBuildRunner'; import { DebuggerInformation } from '@cmt/debug/cmakeDebugger/debuggerConfigureDriver'; import { onBuildSettingsChange, onTestSettingsChange, onPackageSettingsChange } from '@cmt/ui/util'; @@ -622,6 +622,26 @@ export abstract class CMakeDriver implements vscode.Disposable { } } + /** + * Check if the generator to be used differs from what is cached in CMakeCache.txt. + * If so, auto-clean the prior configuration to prevent CMake errors. + */ + protected async _cleanIfGeneratorChanged(newGeneratorName: string | undefined): Promise { + if (!newGeneratorName) { + return; + } + const cachePath = this.cachePath; + if (!await fs.exists(cachePath)) { + return; + } + const cache = await CMakeCache.fromPath(cachePath); + const cachedGenerator = cache.get('CMAKE_GENERATOR'); + if (cachedGenerator && cachedGenerator.value !== newGeneratorName) { + log.info(localize('generator.changed', 'Generator changed from {0} to {1}; cleaning prior configuration', cachedGenerator.value, newGeneratorName)); + await this._cleanPriorConfiguration(); + } + } + /** * Remove the entire build directory. */ @@ -802,6 +822,7 @@ export abstract class CMakeDriver implements vscode.Disposable { if (needsCleanIfKitChange && (newBinaryDir === oldBinaryDir)) { await this._cleanPriorConfiguration(); } + await this._cleanIfGeneratorChanged(this._generator?.name); }); } @@ -812,14 +833,8 @@ export abstract class CMakeDriver implements vscode.Disposable { this._kitEnvironmentVariables = await effectiveKitEnvironment(kit, this.expansionOptions); // Place a kit preferred generator at the front of the list - // For VS kits that don't have preferredGenerator (e.g., scanned before a VS version was added), - // try to derive it from the VS installation. - let kitPreferredGenerator = kit.preferredGenerator; - if (!kitPreferredGenerator && kit.visualStudio) { - kitPreferredGenerator = await getVsKitPreferredGenerator(kit); - } - if (kitPreferredGenerator) { - preferredGenerators.unshift(kitPreferredGenerator); + if (kit.preferredGenerator) { + preferredGenerators.unshift(kit.preferredGenerator); } // If no preferred generator is defined by the current kit or the user settings, diff --git a/src/kits/kit.ts b/src/kits/kit.ts index 9e0b49dd7..17bc0fa2d 100644 --- a/src/kits/kit.ts +++ b/src/kits/kit.ts @@ -832,60 +832,6 @@ const VsGenerators: { [key: string]: string } = { 18: 'Visual Studio 18 2026' }; -/** - * Get the CMake generator name for a given Visual Studio version - * @param version The major version of Visual Studio (e.g., '17' for VS 2022, '18' for VS 2026) - * @returns The CMake generator name, or undefined if the version is not recognized - */ -export function vsGeneratorForVersion(version: string): string | undefined { - return VsGenerators[version]; -} - -/** - * Get the preferred CMake generator for a Visual Studio kit. - * This is useful for kits that were scanned before a VS version was added to the VsGenerators mapping. - * @param kit The Visual Studio kit - * @returns The preferred generator, or undefined if the kit is not a VS kit or the VS version is not recognized - */ -export async function getVsKitPreferredGenerator(kit: Kit): Promise { - if (!kit.visualStudio || !kit.visualStudioArchitecture) { - return undefined; - } - - // If the kit already has a preferredGenerator, return it - if (kit.preferredGenerator) { - return kit.preferredGenerator; - } - - // Try to derive the preferredGenerator from the VS installation - const vsInstall = await getVSInstallForKit(kit); - if (!vsInstall) { - return undefined; - } - - const version = /^(\d+)+./.exec(vsInstall.installationVersion); - if (!version) { - return undefined; - } - - const generatorName = VsGenerators[version[1]]; - if (!generatorName) { - return undefined; - } - - const majorVersion = parseInt(vsInstall.installationVersion); - const hostArch = kit.visualStudioArchitecture; - const host: string = hostArch.toLowerCase().replace(/ /g, "").startsWith("host=") ? hostArch : "host=" + hostArch; - // For VS kits, use the hostArch as the target platform (x64 -> x64) - const targetArch = hostArch; - - return { - name: generatorName, - platform: generatorPlatformFromVSArch[targetArch] as string || targetArch, - toolset: majorVersion < 15 ? undefined : host - }; -} - /** * Try to get a VSKit from a VS installation and architecture * @param inst A VS installation from vswhere diff --git a/test/unit-tests/generator-mismatch.test.ts b/test/unit-tests/generator-mismatch.test.ts new file mode 100644 index 000000000..262a0056c --- /dev/null +++ b/test/unit-tests/generator-mismatch.test.ts @@ -0,0 +1,101 @@ +/* eslint-disable no-unused-expressions */ +import { expect } from 'chai'; +import { CMakeCache, CacheEntry } from '@cmt/cache'; + +suite('Generator mismatch detection', () => { + // Test the core logic that _cleanIfGeneratorChanged relies on: + // reading CMAKE_GENERATOR from cache and comparing with new generator + + test('Detects generator mismatch when cache has Ninja but VS generator selected', () => { + const cacheContent = [ + '# This is the CMakeCache file.', + 'CMAKE_GENERATOR:INTERNAL=Ninja', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + expect(cachedGenerator.value).to.eq('Ninja'); + // Simulating the comparison in _cleanIfGeneratorChanged + const newGenerator = 'Visual Studio 18 2026'; + expect(cachedGenerator.value).to.not.eq(newGenerator); + }); + + test('No mismatch when cache generator matches selected generator', () => { + const cacheContent = [ + 'CMAKE_GENERATOR:INTERNAL=Ninja', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + expect(cachedGenerator.value).to.eq('Ninja'); + }); + + test('No mismatch detection when cache is empty', () => { + const cacheContent = ''; + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR'); + expect(cachedGenerator).to.be.undefined; + // _cleanIfGeneratorChanged would return early (no cached generator) + }); + + test('No mismatch detection when CMAKE_GENERATOR not in cache', () => { + const cacheContent = [ + 'CMAKE_BUILD_TYPE:STRING=Debug', + 'CMAKE_INSTALL_PREFIX:PATH=/usr/local', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR'); + expect(cachedGenerator).to.be.undefined; + }); + + test('Detects mismatch with Visual Studio generators of different versions', () => { + const cacheContent = [ + 'CMAKE_GENERATOR:INTERNAL=Visual Studio 17 2022', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + const newGenerator = 'Visual Studio 18 2026'; + expect(cachedGenerator.value).to.not.eq(newGenerator); + }); + + test('No mismatch when both are Visual Studio 18 2026', () => { + const cacheContent = [ + 'CMAKE_GENERATOR:INTERNAL=Visual Studio 18 2026', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + expect(cachedGenerator.value).to.eq('Visual Studio 18 2026'); + }); + + test('Detects mismatch switching from VS generator to Ninja', () => { + const cacheContent = [ + 'CMAKE_GENERATOR:INTERNAL=Visual Studio 18 2026', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + const newGenerator = 'Ninja'; + expect(cachedGenerator.value).to.not.eq(newGenerator); + }); + + test('Handles Unix Makefiles generator in cache', () => { + const cacheContent = [ + 'CMAKE_GENERATOR:INTERNAL=Unix Makefiles', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(cachedGenerator).to.not.be.undefined; + expect(cachedGenerator.value).to.eq('Unix Makefiles'); + const newGenerator = 'Ninja'; + expect(cachedGenerator.value).to.not.eq(newGenerator); + }); +}); diff --git a/test/unit-tests/kit-scan.test.ts b/test/unit-tests/kit-scan.test.ts index 3b7fae374..3fdd3a61a 100644 --- a/test/unit-tests/kit-scan.test.ts +++ b/test/unit-tests/kit-scan.test.ts @@ -373,25 +373,130 @@ suite('Kits scan test', () => { }); }); - suite('VS Generator mapping', () => { - test('returns correct generator for VS 2022', () => { - expect(kit.vsGeneratorForVersion('17')).to.eq('Visual Studio 17 2022'); - }); - - test('returns correct generator for VS 2026', () => { - expect(kit.vsGeneratorForVersion('18')).to.eq('Visual Studio 18 2026'); + suite('Kit change detection for generator regression (#4890)', () => { + // These tests verify kitChangeNeedsClean correctly detects generator-related + // kit changes, which is the safety net for the regression fixed in #4890. + + test('Returns false for null old kit (first kit selection)', () => { + const newKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { + name: 'Visual Studio 18 2026', + platform: 'x64', + toolset: 'host=x64' + } + }; + expect(kit.kitChangeNeedsClean(newKit, null)).to.be.false; }); - test('returns correct generator for VS 2019', () => { - expect(kit.vsGeneratorForVersion('16')).to.eq('Visual Studio 16 2019'); + test('Returns true when preferredGenerator changes from undefined to VS generator', () => { + const oldKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true + // preferredGenerator is undefined — simulates kit scanned before VS 2026 mapping + }; + const newKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { + name: 'Visual Studio 18 2026', + platform: 'x64', + toolset: 'host=x64' + } + }; + expect(kit.kitChangeNeedsClean(newKit, oldKit)).to.be.true; }); - test('returns undefined for unknown version', () => { - expect(kit.vsGeneratorForVersion('99')).to.be.undefined; + test('Returns false when kits have identical preferredGenerator', () => { + const generator = { + name: 'Visual Studio 18 2026', + platform: 'x64', + toolset: 'host=x64' + }; + const oldKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { ...generator } + }; + const newKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { ...generator } + }; + expect(kit.kitChangeNeedsClean(newKit, oldKit)).to.be.false; + }); + + test('Returns true when switching from VS generator to no generator', () => { + const oldKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { + name: 'Visual Studio 18 2026', + platform: 'x64', + toolset: 'host=x64' + } + }; + const newKit: kit.Kit = { + name: 'GCC 12.0.0', + isTrusted: true + // No preferredGenerator — typical GCC kit uses Ninja default + }; + expect(kit.kitChangeNeedsClean(newKit, oldKit)).to.be.true; + }); + + test('Returns true when generator name changes between VS versions', () => { + const oldKit: kit.Kit = { + name: 'Visual Studio Community 2022 Release - amd64', + visualStudio: 'VisualStudio.17.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { + name: 'Visual Studio 17 2022', + platform: 'x64', + toolset: 'host=x64' + } + }; + const newKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true, + preferredGenerator: { + name: 'Visual Studio 18 2026', + platform: 'x64', + toolset: 'host=x64' + } + }; + expect(kit.kitChangeNeedsClean(newKit, oldKit)).to.be.true; }); - test('returns correct generator for legacy VS120COMNTOOLS', () => { - expect(kit.vsGeneratorForVersion('VS120COMNTOOLS')).to.eq('Visual Studio 12 2013'); + test('Returns false when both kits have no preferredGenerator', () => { + const oldKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true + }; + const newKit: kit.Kit = { + name: 'Visual Studio Community 2026 Release - amd64', + visualStudio: 'VisualStudio.18.0', + visualStudioArchitecture: 'x64', + isTrusted: true + }; + expect(kit.kitChangeNeedsClean(newKit, oldKit)).to.be.false; }); }); From b802b5646757b33bf0f57fb7fd625deb021620d4 Mon Sep 17 00:00:00 2001 From: Hannia Valera Date: Thu, 16 Apr 2026 12:37:39 -0500 Subject: [PATCH 2/5] updating documentation --- docs/kits.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/kits.md b/docs/kits.md index ee3012847..f437af316 100644 --- a/docs/kits.md +++ b/docs/kits.md @@ -6,11 +6,12 @@ A _kit_ defines project-agnostic and configuration-agnostic info about how to bu - A Visual Studio installation: building for Visual Studio involves more than just finding the necessary compiler executable. Visual C++ requires certain environment variables to be set to tell it how to find and link to the Visual C++ toolchain headers and libraries. - A toolchain file: The low-level way to instruct CMake how to compile and link for a target. CMake Tools handles toolchain files using kits. -Kits are mostly CMake-generator-agnostic (a CMake generator writes the input files for the native build system). Visual Studio kits have a preferred generator that will be used as a fallback to ensure a matching MSBuild and .sln generator are used for the Visual C++ compiler. +Kits are mostly CMake-generator-agnostic (a CMake generator writes the input files for the native build system). The exception is Visual Studio kits: when you [scan for kits](#scan-for-kits), CMake Tools looks up the VS version and sets `preferredGenerator` on the kit to the matching CMake generator (e.g., `"Visual Studio 18 2026"` for VS 2026). This only happens at scan time. If a kit was scanned before CMake Tools knew about that VS version, the kit won't have a `preferredGenerator` and will fall through to whatever default generator is available (usually Ninja). > **Note:** -> * If you use the [Ninja](https://ninja-build.org/) build system, don't worry about Visual Studio CMake Generators. CMake Tools will prefer Ninja if it is present, unless configured otherwise. +> * If you use the [Ninja](https://ninja-build.org/) build system, don't worry about Visual Studio CMake Generators. CMake Tools will prefer Ninja if it is present, unless configured otherwise. A Visual Studio kit without `preferredGenerator` will just use Ninja. > * If you change the active kit while a project is configured, the project configuration will be re-generated with the chosen kit. +> * When the selected generator doesn't match what's already in an existing `CMakeCache.txt`, CMake Tools cleans the prior configuration instead of letting CMake error out. > * Using a kit is recommended but optional. If you don't use a kit, CMake will perform its own automatic detection. ## How kits are found and defined @@ -52,7 +53,7 @@ Update [user-local kits](#user-local-kits) by running **Scan for Kits** from the - CMake tools includes `vswhere.exe`, which it uses to find Visual Studio instances installed on the system. -- For each of `x86`, `amd64`, `x86_amd64`, `x86_arm`, `x86_arm64`, `amd64_x86`, `amd64_arm`, and `amd64_arm64`, CMake Tools checks for installed Visual C++ environments. A kit is generated for each existing MSVC toolchain that is found. +- For each of `x86`, `amd64`, `x86_amd64`, `x86_arm`, `x86_arm64`, `amd64_x86`, `amd64_arm`, and `amd64_arm64`, CMake Tools checks for installed Visual C++ environments. A kit is generated for each existing MSVC toolchain that is found. For known VS versions (2019, 2022, 2026, etc.), the kit gets a `preferredGenerator` pointing at the right CMake generator, like `"Visual Studio 18 2026"`. If CMake Tools doesn't recognize the VS version yet, `preferredGenerator` is left unset and the kit will use Ninja or whatever default is available. **3. Save results to the user-local kits file** @@ -147,7 +148,14 @@ The following additional options may be specified: `preferredGenerator` -> The CMake generator that should be used with this kit if not the default. CMake Tools will still search in `cmake.preferredGenerators` from `settings.json`, but will fall back to this option if no generator from the user settings is available +> The CMake generator to use with this kit if not the default. For Visual Studio kits, this is set during [kit scanning](#scan-for-kits) based on the VS version. When picking a generator, CMake Tools checks these in order: +> +> 1. `cmake.generator` from your settings (wins over everything else) +> 2. `cmake.preferredGenerators` from your settings +> 3. The kit's `preferredGenerator` (set at scan time for VS kits) +> 4. Fallback: Ninja, then Unix Makefiles +> +> If your kit doesn't have a `preferredGenerator` (maybe it was scanned before CMake Tools added support for that VS version), it will just use the fallback. Re-running **Scan for Kits** will pick up the correct generator. `cmakeSettings` From d3d666934319a39d4120718aa96da75f573c33b0 Mon Sep 17 00:00:00 2001 From: Hannia Valera Date: Fri, 17 Apr 2026 16:00:00 -0500 Subject: [PATCH 3/5] improve Visual Studio generator handling and add tests for version mapping - Modify kit scanning logic to derive preferred generators for Visual Studio kits lacking a predefined generator. - Introduce utility functions for mapping Visual Studio versions to CMake generators. - Add unit tests to verify correct generator mapping for various Visual Studio versions. --- CHANGELOG.md | 2 +- docs/kits.md | 9 +++--- src/drivers/cmakeDriver.ts | 28 +++++++++++------ src/kits/kit.ts | 52 ++++++++++++++++++++++++++++++++ test/unit-tests/kit-scan.test.ts | 22 ++++++++++++++ 5 files changed, 99 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb470078a..cdda2499b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Bug Fixes: ## 1.23.51 Bug Fixes: -- Fix regression where VS kits with existing Ninja cache would fail due to generator mismatch after update. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) +- Fix regression where Visual Studio kits with an existing Ninja-based build cache would fail after updating CMake Tools, due to a generator mismatch. Ninja is now preferred again when available. For stale VS kits missing `preferredGenerator`, the VS generator is derived at runtime as a last-resort fallback. A safety net auto-cleans the build directory when a generator mismatch with `CMakeCache.txt` is detected. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) ## 1.23 diff --git a/docs/kits.md b/docs/kits.md index f437af316..b2a300126 100644 --- a/docs/kits.md +++ b/docs/kits.md @@ -6,10 +6,10 @@ A _kit_ defines project-agnostic and configuration-agnostic info about how to bu - A Visual Studio installation: building for Visual Studio involves more than just finding the necessary compiler executable. Visual C++ requires certain environment variables to be set to tell it how to find and link to the Visual C++ toolchain headers and libraries. - A toolchain file: The low-level way to instruct CMake how to compile and link for a target. CMake Tools handles toolchain files using kits. -Kits are mostly CMake-generator-agnostic (a CMake generator writes the input files for the native build system). The exception is Visual Studio kits: when you [scan for kits](#scan-for-kits), CMake Tools looks up the VS version and sets `preferredGenerator` on the kit to the matching CMake generator (e.g., `"Visual Studio 18 2026"` for VS 2026). This only happens at scan time. If a kit was scanned before CMake Tools knew about that VS version, the kit won't have a `preferredGenerator` and will fall through to whatever default generator is available (usually Ninja). +Kits are mostly CMake-generator-agnostic (a CMake generator writes the input files for the native build system). The exception is Visual Studio kits: when you [scan for kits](#scan-for-kits), CMake Tools looks up the VS version and sets `preferredGenerator` on the kit to the matching CMake generator (e.g., `"Visual Studio 18 2026"` for VS 2026). If a kit was scanned before CMake Tools added support for that VS version, the extension derives the correct VS generator at runtime as a last-resort fallback — it is tried only after other generators like Ninja. If the version can't be determined, the kit falls through to default generators. > **Note:** -> * If you use the [Ninja](https://ninja-build.org/) build system, don't worry about Visual Studio CMake Generators. CMake Tools will prefer Ninja if it is present, unless configured otherwise. A Visual Studio kit without `preferredGenerator` will just use Ninja. +> * If you use the [Ninja](https://ninja-build.org/) build system, don't worry about Visual Studio CMake Generators. CMake Tools will prefer Ninja if it is present, unless configured otherwise or the kit has a `preferredGenerator` set at scan time. To explicitly use a specific generator, set `cmake.generator` in your settings. > * If you change the active kit while a project is configured, the project configuration will be re-generated with the chosen kit. > * When the selected generator doesn't match what's already in an existing `CMakeCache.txt`, CMake Tools cleans the prior configuration instead of letting CMake error out. > * Using a kit is recommended but optional. If you don't use a kit, CMake will perform its own automatic detection. @@ -53,7 +53,7 @@ Update [user-local kits](#user-local-kits) by running **Scan for Kits** from the - CMake tools includes `vswhere.exe`, which it uses to find Visual Studio instances installed on the system. -- For each of `x86`, `amd64`, `x86_amd64`, `x86_arm`, `x86_arm64`, `amd64_x86`, `amd64_arm`, and `amd64_arm64`, CMake Tools checks for installed Visual C++ environments. A kit is generated for each existing MSVC toolchain that is found. For known VS versions (2019, 2022, 2026, etc.), the kit gets a `preferredGenerator` pointing at the right CMake generator, like `"Visual Studio 18 2026"`. If CMake Tools doesn't recognize the VS version yet, `preferredGenerator` is left unset and the kit will use Ninja or whatever default is available. +- For each of `x86`, `amd64`, `x86_amd64`, `x86_arm`, `x86_arm64`, `amd64_x86`, `amd64_arm`, and `amd64_arm64`, CMake Tools checks for installed Visual C++ environments. A kit is generated for each existing MSVC toolchain that is found. For known VS versions (2019, 2022, 2026, etc.), the kit gets a `preferredGenerator` pointing at the right CMake generator, like `"Visual Studio 18 2026"`. If the VS version wasn't recognized at scan time, CMake Tools derives the correct generator at runtime as a last-resort fallback. Re-running **Scan for Kits** will set `preferredGenerator` permanently. **3. Save results to the user-local kits file** @@ -154,8 +154,9 @@ The following additional options may be specified: > 2. `cmake.preferredGenerators` from your settings > 3. The kit's `preferredGenerator` (set at scan time for VS kits) > 4. Fallback: Ninja, then Unix Makefiles +> 5. For VS kits without `preferredGenerator`: the derived VS generator (last-resort fallback) > -> If your kit doesn't have a `preferredGenerator` (maybe it was scanned before CMake Tools added support for that VS version), it will just use the fallback. Re-running **Scan for Kits** will pick up the correct generator. +> If a VS kit was scanned before the VS version mapping existed, CMake Tools derives the correct generator at runtime but only as a last resort — Ninja and Unix Makefiles are tried first. To use the VS generator by default, re-run **Scan for Kits** or set `cmake.generator` in your settings. `cmakeSettings` diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts index da5e4ffbb..7ad1d12b1 100644 --- a/src/drivers/cmakeDriver.ts +++ b/src/drivers/cmakeDriver.ts @@ -16,7 +16,7 @@ import { CMakeOutputConsumer } from '@cmt/diagnostics/cmake'; import { RawDiagnosticParser } from '@cmt/diagnostics/util'; import { ProgressMessage } from '@cmt/drivers/drivers'; import * as expand from '@cmt/expand'; -import { CMakeGenerator, effectiveKitEnvironment, Kit, kitChangeNeedsClean, KitDetect, getKitDetect, getVSKitEnvironment } from '@cmt/kits/kit'; +import { CMakeGenerator, effectiveKitEnvironment, Kit, kitChangeNeedsClean, KitDetect, getKitDetect, getVSKitEnvironment, getVsKitPreferredGenerator } from '@cmt/kits/kit'; import * as logging from '@cmt/logging'; import paths from '@cmt/paths'; import { fs } from '@cmt/pr'; @@ -624,22 +624,22 @@ export abstract class CMakeDriver implements vscode.Disposable { /** * Check if the generator to be used differs from what is cached in CMakeCache.txt. - * If so, auto-clean the prior configuration to prevent CMake errors. */ - protected async _cleanIfGeneratorChanged(newGeneratorName: string | undefined): Promise { + protected async _hasGeneratorChanged(newGeneratorName: string | undefined): Promise { if (!newGeneratorName) { - return; + return false; } const cachePath = this.cachePath; if (!await fs.exists(cachePath)) { - return; + return false; } const cache = await CMakeCache.fromPath(cachePath); const cachedGenerator = cache.get('CMAKE_GENERATOR'); if (cachedGenerator && cachedGenerator.value !== newGeneratorName) { log.info(localize('generator.changed', 'Generator changed from {0} to {1}; cleaning prior configuration', cachedGenerator.value, newGeneratorName)); - await this._cleanPriorConfiguration(); + return true; } + return false; } /** @@ -819,10 +819,10 @@ export abstract class CMakeDriver implements vscode.Disposable { await this._refreshExpansions(); const scope = this.workspaceFolder ? vscode.Uri.file(this.workspaceFolder) : undefined; const newBinaryDir = util.lightNormalizePath(await expand.expandString(this.config.buildDirectory(this.isMultiProject, scope), this.expansionOptions)); - if (needsCleanIfKitChange && (newBinaryDir === oldBinaryDir)) { + const generatorChanged = await this._hasGeneratorChanged(this._generator?.name); + if ((needsCleanIfKitChange || generatorChanged) && (newBinaryDir === oldBinaryDir)) { await this._cleanPriorConfiguration(); } - await this._cleanIfGeneratorChanged(this._generator?.name); }); } @@ -832,7 +832,7 @@ export abstract class CMakeDriver implements vscode.Disposable { log.debug(localize('cmakedriver.kit.set.to', 'CMakeDriver Kit set to {0}', kit.name)); this._kitEnvironmentVariables = await effectiveKitEnvironment(kit, this.expansionOptions); - // Place a kit preferred generator at the front of the list + // Place a kit preferred generator at the front of the list. if (kit.preferredGenerator) { preferredGenerators.unshift(kit.preferredGenerator); } @@ -848,6 +848,16 @@ export abstract class CMakeDriver implements vscode.Disposable { preferredGenerators.push({ name: "Unix Makefiles" }); } + // For VS kits that don't have preferredGenerator (e.g., scanned before + // a VS version was added), derive the VS generator as a last-resort + // fallback so it is tried only after Ninja / Unix Makefiles. + if (!kit.preferredGenerator && kit.visualStudio) { + const derived = await getVsKitPreferredGenerator(kit); + if (derived) { + preferredGenerators.push(derived); + } + } + // Use the "best generator" logic only if the user did not define a particular // generator to be used via the `cmake.generator` setting. if (this.config.generator) { diff --git a/src/kits/kit.ts b/src/kits/kit.ts index 17bc0fa2d..1e8451b68 100644 --- a/src/kits/kit.ts +++ b/src/kits/kit.ts @@ -832,6 +832,58 @@ const VsGenerators: { [key: string]: string } = { 18: 'Visual Studio 18 2026' }; +/** + * Get the CMake generator name for a given Visual Studio major version. + * @param version The major version string (e.g., '17', '18') + * @returns The CMake generator name, or undefined if the version is not recognized + */ +export function vsGeneratorForVersion(version: string): string | undefined { + return VsGenerators[version]; +} + +/** + * Get the preferred CMake generator for a Visual Studio kit. + * This is useful for kits that were scanned before a VS version was added to the VsGenerators mapping. + * @param kit The Visual Studio kit + * @returns The preferred generator, or undefined if the kit is not a VS kit or the VS version is not recognized + */ +export async function getVsKitPreferredGenerator(kit: Kit): Promise { + if (!kit.visualStudio || !kit.visualStudioArchitecture) { + return undefined; + } + + // If the kit already has a preferredGenerator, return it + if (kit.preferredGenerator) { + return kit.preferredGenerator; + } + + // Try to derive the preferredGenerator from the VS installation + const vsInstall = await getVSInstallForKit(kit); + if (!vsInstall) { + return undefined; + } + + const version = /^(\d+)+./.exec(vsInstall.installationVersion); + if (!version) { + return undefined; + } + + const generatorName = VsGenerators[version[1]]; + if (!generatorName) { + return undefined; + } + + const majorVersion = parseInt(vsInstall.installationVersion); + const hostArch = kit.visualStudioArchitecture; + const host: string = hostArch.toLowerCase().replace(/ /g, "").startsWith("host=") ? hostArch : "host=" + hostArch; + + return { + name: generatorName, + platform: generatorPlatformFromVSArch[hostArch] as string || hostArch, + toolset: majorVersion < 15 ? undefined : host + }; +} + /** * Try to get a VSKit from a VS installation and architecture * @param inst A VS installation from vswhere diff --git a/test/unit-tests/kit-scan.test.ts b/test/unit-tests/kit-scan.test.ts index 3fdd3a61a..033caeb27 100644 --- a/test/unit-tests/kit-scan.test.ts +++ b/test/unit-tests/kit-scan.test.ts @@ -373,6 +373,28 @@ suite('Kits scan test', () => { }); }); + suite('VS Generator mapping', () => { + test('returns correct generator for VS 2022', () => { + expect(kit.vsGeneratorForVersion('17')).to.eq('Visual Studio 17 2022'); + }); + + test('returns correct generator for VS 2026', () => { + expect(kit.vsGeneratorForVersion('18')).to.eq('Visual Studio 18 2026'); + }); + + test('returns correct generator for VS 2019', () => { + expect(kit.vsGeneratorForVersion('16')).to.eq('Visual Studio 16 2019'); + }); + + test('returns undefined for unknown version', () => { + expect(kit.vsGeneratorForVersion('99')).to.be.undefined; + }); + + test('returns correct generator for legacy VS120COMNTOOLS', () => { + expect(kit.vsGeneratorForVersion('VS120COMNTOOLS')).to.eq('Visual Studio 12 2013'); + }); + }); + suite('Kit change detection for generator regression (#4890)', () => { // These tests verify kitChangeNeedsClean correctly detects generator-related // kit changes, which is the safety net for the regression fixed in #4890. From bf8b73384eaa71a3ff03f58242d08bf43b978203 Mon Sep 17 00:00:00 2001 From: Hannia Valera Date: Mon, 20 Apr 2026 12:03:05 -0500 Subject: [PATCH 4/5] took in more feedback --- CHANGELOG.md | 2 +- docs/kits.md | 12 +- src/drivers/cmakeDriver.ts | 30 +++- test/unit-tests/generator-mismatch.test.ts | 160 ++++++++++----------- 4 files changed, 110 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdda2499b..2b3cb3fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Bug Fixes: ## 1.23.51 Bug Fixes: -- Fix regression where Visual Studio kits with an existing Ninja-based build cache would fail after updating CMake Tools, due to a generator mismatch. Ninja is now preferred again when available. For stale VS kits missing `preferredGenerator`, the VS generator is derived at runtime as a last-resort fallback. A safety net auto-cleans the build directory when a generator mismatch with `CMakeCache.txt` is detected. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) +- Fix regression where Visual Studio kits with an existing Ninja-based build cache would fail after updating CMake Tools, due to a generator mismatch. Ninja is now preferred again when available. For stale VS kits missing `preferredGenerator`, the VS generator is derived at runtime as a last-resort fallback. A safety net auto-cleans the build directory when a generator mismatch with `CMakeCache.txt` is detected — now also covering driver reloads triggered by `cmake.generator` / `cmake.preferredGenerators` setting changes, not just kit switches. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) ## 1.23 diff --git a/docs/kits.md b/docs/kits.md index b2a300126..3f8f01a27 100644 --- a/docs/kits.md +++ b/docs/kits.md @@ -150,13 +150,13 @@ The following additional options may be specified: > The CMake generator to use with this kit if not the default. For Visual Studio kits, this is set during [kit scanning](#scan-for-kits) based on the VS version. When picking a generator, CMake Tools checks these in order: > -> 1. `cmake.generator` from your settings (wins over everything else) -> 2. `cmake.preferredGenerators` from your settings -> 3. The kit's `preferredGenerator` (set at scan time for VS kits) -> 4. Fallback: Ninja, then Unix Makefiles -> 5. For VS kits without `preferredGenerator`: the derived VS generator (last-resort fallback) +> 1. `cmake.generator` from your settings — if set, this wins outright; nothing below is consulted. +> 2. The kit's `preferredGenerator` (set at scan time for VS kits). +> 3. `cmake.preferredGenerators` from your settings, in order. +> 4. `Ninja`, then `Unix Makefiles` — only consulted when neither #2 nor #3 produced any candidate (i.e. the kit has no `preferredGenerator` and `cmake.preferredGenerators` is empty). +> 5. For VS kits that have no `preferredGenerator` of their own: the VS generator derived at runtime from the kit's VS version — pushed to the end of the candidate list, so it's tried after the Ninja/Unix Makefiles fallback when both apply. > -> If a VS kit was scanned before the VS version mapping existed, CMake Tools derives the correct generator at runtime but only as a last resort — Ninja and Unix Makefiles are tried first. To use the VS generator by default, re-run **Scan for Kits** or set `cmake.generator` in your settings. +> If a VS kit was scanned before the VS version mapping existed, CMake Tools derives the correct generator at runtime (#5) — but only after Ninja and Unix Makefiles. To make the VS generator the unconditional choice, either re-run **Scan for Kits** so the kit gets its own `preferredGenerator`, or set `cmake.generator` in your settings. `cmakeSettings` diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts index 7ad1d12b1..865cf916d 100644 --- a/src/drivers/cmakeDriver.ts +++ b/src/drivers/cmakeDriver.ts @@ -148,6 +148,18 @@ export interface InstallPath { * * This class defines the basis for what a driver must implement to work. */ +/** + * Compare a new generator name against a cached CMAKE_GENERATOR value. + * Returns true when both are defined and differ (i.e. a mismatch that needs cleaning). + * Pure function — no I/O — exported for direct unit testing. + */ +export function generatorMismatch(newGeneratorName: string | undefined, cachedGeneratorValue: string | undefined): boolean { + if (!newGeneratorName || !cachedGeneratorValue) { + return false; + } + return cachedGeneratorValue !== newGeneratorName; +} + export abstract class CMakeDriver implements vscode.Disposable { /** * Do the configuration process for the current project. @@ -635,8 +647,8 @@ export abstract class CMakeDriver implements vscode.Disposable { } const cache = await CMakeCache.fromPath(cachePath); const cachedGenerator = cache.get('CMAKE_GENERATOR'); - if (cachedGenerator && cachedGenerator.value !== newGeneratorName) { - log.info(localize('generator.changed', 'Generator changed from {0} to {1}; cleaning prior configuration', cachedGenerator.value, newGeneratorName)); + if (generatorMismatch(newGeneratorName, cachedGenerator?.value)) { + log.info(localize('generator.changed', 'Generator changed from {0} to {1}; cleaning prior configuration', cachedGenerator!.value, newGeneratorName)); return true; } return false; @@ -1631,6 +1643,20 @@ export abstract class CMakeDriver implements vscode.Disposable { return { exitCode: -2, resultType: ConfigureResultType.Other }; } + // Safety net for the kits/variants path: if a setting change + // (cmake.generator / cmake.preferredGenerators) reloaded the driver + // without going through setKit(), the cached CMAKE_GENERATOR may + // differ from this._generator. Clean the prior configuration here + // so CMake doesn't fail with a "generator changed" error. + // Presets handle this in setConfigurePreset via configurePresetChangeNeedsClean. + if (!this.useCMakePresets + && !showCommandOnly + && !shouldUseCachedConfiguration + && trigger !== ConfigureTrigger.configureWithCache + && await this._hasGeneratorChanged(this._generator?.name)) { + await this._cleanPriorConfiguration(); + } + let expanded_flags: string[]; let defaultPresetName: string | undefined; if (this.useCMakePresets) { diff --git a/test/unit-tests/generator-mismatch.test.ts b/test/unit-tests/generator-mismatch.test.ts index 262a0056c..f98b1e2db 100644 --- a/test/unit-tests/generator-mismatch.test.ts +++ b/test/unit-tests/generator-mismatch.test.ts @@ -1,101 +1,91 @@ /* eslint-disable no-unused-expressions */ import { expect } from 'chai'; import { CMakeCache, CacheEntry } from '@cmt/cache'; +import { generatorMismatch } from '@cmt/drivers/cmakeDriver'; suite('Generator mismatch detection', () => { - // Test the core logic that _cleanIfGeneratorChanged relies on: - // reading CMAKE_GENERATOR from cache and comparing with new generator + suite('generatorMismatch (pure helper)', () => { + test('Returns true when generators differ', () => { + expect(generatorMismatch('Visual Studio 18 2026', 'Ninja')).to.be.true; + }); - test('Detects generator mismatch when cache has Ninja but VS generator selected', () => { - const cacheContent = [ - '# This is the CMakeCache file.', - 'CMAKE_GENERATOR:INTERNAL=Ninja', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - expect(cachedGenerator.value).to.eq('Ninja'); - // Simulating the comparison in _cleanIfGeneratorChanged - const newGenerator = 'Visual Studio 18 2026'; - expect(cachedGenerator.value).to.not.eq(newGenerator); - }); + test('Returns false when generators match', () => { + expect(generatorMismatch('Ninja', 'Ninja')).to.be.false; + }); - test('No mismatch when cache generator matches selected generator', () => { - const cacheContent = [ - 'CMAKE_GENERATOR:INTERNAL=Ninja', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - expect(cachedGenerator.value).to.eq('Ninja'); - }); + test('Returns false when new generator is undefined', () => { + expect(generatorMismatch(undefined, 'Ninja')).to.be.false; + }); - test('No mismatch detection when cache is empty', () => { - const cacheContent = ''; - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR'); - expect(cachedGenerator).to.be.undefined; - // _cleanIfGeneratorChanged would return early (no cached generator) - }); + test('Returns false when cached generator is undefined', () => { + expect(generatorMismatch('Ninja', undefined)).to.be.false; + }); - test('No mismatch detection when CMAKE_GENERATOR not in cache', () => { - const cacheContent = [ - 'CMAKE_BUILD_TYPE:STRING=Debug', - 'CMAKE_INSTALL_PREFIX:PATH=/usr/local', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR'); - expect(cachedGenerator).to.be.undefined; - }); + test('Returns false when both are undefined', () => { + expect(generatorMismatch(undefined, undefined)).to.be.false; + }); - test('Detects mismatch with Visual Studio generators of different versions', () => { - const cacheContent = [ - 'CMAKE_GENERATOR:INTERNAL=Visual Studio 17 2022', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - const newGenerator = 'Visual Studio 18 2026'; - expect(cachedGenerator.value).to.not.eq(newGenerator); - }); + test('Returns false when cached value is empty string', () => { + expect(generatorMismatch('Ninja', '')).to.be.false; + }); - test('No mismatch when both are Visual Studio 18 2026', () => { - const cacheContent = [ - 'CMAKE_GENERATOR:INTERNAL=Visual Studio 18 2026', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - expect(cachedGenerator.value).to.eq('Visual Studio 18 2026'); - }); + test('Returns true for different VS versions', () => { + expect(generatorMismatch('Visual Studio 18 2026', 'Visual Studio 17 2022')).to.be.true; + }); - test('Detects mismatch switching from VS generator to Ninja', () => { - const cacheContent = [ - 'CMAKE_GENERATOR:INTERNAL=Visual Studio 18 2026', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - const newGenerator = 'Ninja'; - expect(cachedGenerator.value).to.not.eq(newGenerator); + test('Returns true switching from VS generator to Ninja', () => { + expect(generatorMismatch('Ninja', 'Visual Studio 18 2026')).to.be.true; + }); + + test('Returns true switching from Unix Makefiles to Ninja', () => { + expect(generatorMismatch('Ninja', 'Unix Makefiles')).to.be.true; + }); }); - test('Handles Unix Makefiles generator in cache', () => { - const cacheContent = [ - 'CMAKE_GENERATOR:INTERNAL=Unix Makefiles', - '' - ].join('\n'); - const entries = CMakeCache.parseCache(cacheContent); - const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; - expect(cachedGenerator).to.not.be.undefined; - expect(cachedGenerator.value).to.eq('Unix Makefiles'); - const newGenerator = 'Ninja'; - expect(cachedGenerator.value).to.not.eq(newGenerator); + suite('CMakeCache.parseCache integration', () => { + test('Detects mismatch via parsed cache (Ninja cached, VS selected)', () => { + const cacheContent = [ + '# This is the CMakeCache file.', + 'CMAKE_GENERATOR:INTERNAL=Ninja', + '' + ].join('\n'); + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(generatorMismatch('Visual Studio 18 2026', cachedGenerator.value)).to.be.true; + }); + + test('No mismatch when cached generator matches selected', () => { + const cacheContent = 'CMAKE_GENERATOR:INTERNAL=Ninja\n'; + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(generatorMismatch('Ninja', cachedGenerator.value)).to.be.false; + }); + + test('No mismatch when CMAKE_GENERATOR absent from cache', () => { + const cacheContent = 'CMAKE_BUILD_TYPE:STRING=Debug\n'; + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR'); + expect(generatorMismatch('Ninja', cachedGenerator?.value)).to.be.false; + }); + + test('No mismatch when cache is empty', () => { + const entries = CMakeCache.parseCache(''); + const cachedGenerator = entries.get('CMAKE_GENERATOR'); + expect(generatorMismatch('Ninja', cachedGenerator?.value)).to.be.false; + }); + + test('Detects mismatch between two VS generator versions via cache', () => { + const cacheContent = 'CMAKE_GENERATOR:INTERNAL=Visual Studio 17 2022\n'; + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(generatorMismatch('Visual Studio 18 2026', cachedGenerator.value)).to.be.true; + }); + + test('Detects mismatch when switching from Unix Makefiles cached to Ninja', () => { + const cacheContent = 'CMAKE_GENERATOR:INTERNAL=Unix Makefiles\n'; + const entries = CMakeCache.parseCache(cacheContent); + const cachedGenerator = entries.get('CMAKE_GENERATOR') as CacheEntry; + expect(generatorMismatch('Ninja', cachedGenerator.value)).to.be.true; + }); }); }); From a33e240f2a956461062fbea151d24aa0ca897372 Mon Sep 17 00:00:00 2001 From: Hannia Valera Date: Wed, 22 Apr 2026 10:20:57 -0500 Subject: [PATCH 5/5] adjust changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 021cfcb30..64f34e12c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,10 @@ Bug Fixes: - Fix kit detection returning "unknown vendor" when using clang-cl compiler. [#4638](https://github.com/microsoft/vscode-cmake-tools/issues/4638) - Update testing framework to fix bugs when running tests of CMake Tools without a reliable internet connection. [#4891](https://github.com/microsoft/vscode-cmake-tools/pull/4891) [@cwalther](https://github.com/cwalther) -## 1.23.51 +## 1.23.52 Bug Fixes: -- Fix regression where Visual Studio kits with an existing Ninja-based build cache would fail after updating CMake Tools, due to a generator mismatch. Ninja is now preferred again when available. For stale VS kits missing `preferredGenerator`, the VS generator is derived at runtime as a last-resort fallback. A safety net auto-cleans the build directory when a generator mismatch with `CMakeCache.txt` is detected — now also covering driver reloads triggered by `cmake.generator` / `cmake.preferredGenerators` setting changes, not just kit switches. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) +- Fix regression where Visual Studio kits with an existing Ninja-based build cache would fail due to a generator mismatch. Ninja is now preferred again when available, stale VS kits derive the correct generator at runtime as a fallback, and the build directory is auto-cleaned on generator mismatches. [#4890](https://github.com/microsoft/vscode-cmake-tools/issues/4890) ## 1.23