Skip to content

Commit 7138e9e

Browse files
Omotolamcodilla
andauthored
Fix for removing stale kits during Scan (#4856)
* add new setting for removing stale kits * add new setting for removing stale kits * add accidentally removed scan * readd accidentally removed scan --------- Co-authored-by: MacGyver Codilla <[email protected]>
1 parent dccba3e commit 7138e9e

12 files changed

Lines changed: 124 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Improvements:
3636
- Append `cmake` to diagnostics that this extension contributes to the Problems Pane. [PR #4766](https://github.com/microsoft/vscode-cmake-tools/pull/4766)
3737
- Set the `VSCODE_CMAKE_TOOLS` environment variable for all spawned subprocesses so that `CMakeLists.txt` can detect when CMake is run from VS Code. [#4233](https://github.com/microsoft/vscode-cmake-tools/issues/4233)
3838
- Honor `debugger.workingDirectory` from the CMake File API when debugging a target, so that the `DEBUGGER_WORKING_DIRECTORY` target property is used as the debugger working directory. [#4595](https://github.com/microsoft/vscode-cmake-tools/issues/4595)
39+
- Add `cmake.removeStaleKitsOnScan` setting to optionally remove stale compiler kits from the kit picker after a "Scan for Kits" when they are no longer rediscovered. This is useful after compiler upgrades that leave older versions outside `PATH`. Set `"keep": true` in a kit entry to prevent automatic removal. [#3852](https://github.com/microsoft/vscode-cmake-tools/issues/3852)
3940
- Add `pr-readiness` Copilot skill to verify PRs have a descriptive title, meaningful description, and a properly formatted CHANGELOG entry. [#4862](https://github.com/microsoft/vscode-cmake-tools/pull/4862)
4041

4142
Bug Fixes:

docs/cmake-settings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Options that support substitution, in the table below, allow variable references
4848
| `cmake.deleteBuildDirOnCleanConfigure` | If `true`, delete build directory during clean configure. | `false` | no |
4949
| `cmake.emscriptenSearchDirs` | List of paths to search for Emscripten. | `[]` | no |
5050
| `cmake.enableAutomaticKitScan` | Enable automatic kit scanning. | `true` | no |
51+
| `cmake.removeStaleKitsOnScan` | If `true`, a full **Scan for Kits** run removes compiler-based kits from `cmake-tools-kits.json` when they are no longer rediscovered. This is useful after compiler upgrades that leave older versions installed outside `PATH`. Set `"keep": true` in a kit entry to preserve it. This setting does not affect **Scan recursively for kits in specific directories**. | `false` | no |
5152
| `cmake.enabledOutputParsers` | List of enabled output parsers. | `["cmake", "gcc", "gnuld", "msvc", "ghs", "diab", "iwyu"]` | no |
5253
| `cmake.additionalBuildProblemMatchers` | Array of user-defined problem matchers for build output. Each entry has `name`, `regexp`, and optional capture group indices (`file`, `line`, `column`, `severity`, `message`, `code`). See [Additional Build Problem Matchers](#additional-build-problem-matchers) below. | `[]` | no |
5354
| `cmake.enableLanguageServices` | If `true`, enable CMake language services. | `true` | no |

docs/kits.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ Update [user-local kits](#user-local-kits) by running **Scan for Kits** from the
5959
- The [user-local kit](#user-local-kits) `cmake-tools-kits.json` file is updated with the new kit information.
6060

6161
> **Warning:**
62-
> The name of each kit is generated from the kit compiler and version information. Kits with the same name will be overwritten. To prevent custom kits from being overwritten, give them unique names. CMake Tools will not delete entries from `cmake-tools-kits.json`. It only adds and updates existing ones.
62+
> The name of each kit is generated from the kit compiler and version information. Kits with the same name will be overwritten. To prevent custom kits from being overwritten, give them unique names.
63+
>
64+
> By default, scans preserve existing entries in `cmake-tools-kits.json`. If `cmake.removeStaleKitsOnScan` is enabled, compiler-based kits (those with a `compilers` field) that are no longer rediscovered during a full scan are removed automatically. Set `"keep": true` in a kit entry to preserve it even when this cleanup is enabled. Kits without a `compilers` field (toolchain-only kits and Visual Studio kits) are always preserved.
6365
6466
## Kit options
6567

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4112,6 +4112,12 @@
41124112
"description": "%cmake-tools.configuration.cmake.enableAutomaticKitScan.description%",
41134113
"scope": "resource"
41144114
},
4115+
"cmake.removeStaleKitsOnScan": {
4116+
"type": "boolean",
4117+
"default": false,
4118+
"description": "%cmake-tools.configuration.cmake.removeStaleKitsOnScan.description%",
4119+
"scope": "window"
4120+
},
41154121
"cmake.enableLanguageServices": {
41164122
"type": "boolean",
41174123
"default": true,

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@
380380
]
381381
},
382382
"cmake-tools.configuration.cmake.enableAutomaticKitScan.description": "Enable automatic scanning for kits when a kit isn't selected. This will only take affect when CMake Presets aren't being used.",
383+
"cmake-tools.configuration.cmake.removeStaleKitsOnScan.description": "Remove compiler-based kits from the user kits file during a full kit scan when they are no longer rediscovered. Set \"keep\": true in a kit entry to preserve it.",
383384
"cmake-tools.configuration.cmake.enableLanguageServices.description": "Enable language services for CMake files. This will enable syntax highlighting, code completion, and other features.",
384385
"cmake-tools.configuration.cmake.preRunCoverageTarget.description": "Target to build before running tests with coverage using the test explorer",
385386
"cmake-tools.configuration.cmake.postRunCoverageTarget.description": "Target to build after running tests with coverage using the test explorer",

src/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export interface ExtensionConfigurationSettings {
248248
automaticReconfigure: boolean;
249249
pinnedCommands: string[];
250250
enableAutomaticKitScan: boolean;
251+
removeStaleKitsOnScan: boolean;
251252
enableLanguageServices: boolean;
252253
preRunCoverageTarget: string | null;
253254
postRunCoverageTarget: string | null;
@@ -642,6 +643,10 @@ export class ConfigurationReader implements vscode.Disposable {
642643
return this.configData.enableAutomaticKitScan;
643644
}
644645

646+
get removeStaleKitsOnScan(): boolean {
647+
return this.configData.removeStaleKitsOnScan;
648+
}
649+
645650
get enableLanguageServices(): boolean {
646651
return this.configData.enableLanguageServices;
647652
}
@@ -740,6 +745,7 @@ export class ConfigurationReader implements vscode.Disposable {
740745
automaticReconfigure: new vscode.EventEmitter<boolean>(),
741746
pinnedCommands: new vscode.EventEmitter<string[]>(),
742747
enableAutomaticKitScan: new vscode.EventEmitter<boolean>(),
748+
removeStaleKitsOnScan: new vscode.EventEmitter<boolean>(),
743749
enableLanguageServices: new vscode.EventEmitter<boolean>(),
744750
preRunCoverageTarget: new vscode.EventEmitter<string | null>(),
745751
postRunCoverageTarget: new vscode.EventEmitter<string | null>(),

src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,9 @@ export class ExtensionManager implements vscode.Disposable {
10911091
}
10921092
}
10931093

1094-
const duplicateRemoved = await KitsController.scanForKits(cmakePath);
1094+
const duplicateRemoved = await KitsController.scanForKits(cmakePath, {
1095+
removeStaleCompilerKits: this.workspaceConfig.removeStaleKitsOnScan
1096+
});
10951097
if (duplicateRemoved) {
10961098
// Check each project. If there is an active kit set and if it is of the old definition, unset the kit.
10971099
for (const project of this.projectController.getAllCMakeProjects()) {

src/kits/kit.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1341,7 +1341,9 @@ export async function scanForKitsIfNeeded(project: CMakeProject): Promise<boolea
13411341

13421342
// action === 'scan'
13431343
log.info(localize('silent.kits.rescan', 'Detected kits definition version change from {0} to {1}. Silently scanning for kits.', kitsVersionSaved, kitsVersionCurrent));
1344-
await kitsController.KitsController.scanForKits(await project.getCMakePathofProject());
1344+
await kitsController.KitsController.scanForKits(await project.getCMakePathofProject(), {
1345+
removeStaleCompilerKits: project.workspaceContext.config.removeStaleKitsOnScan
1346+
});
13451347
await project.workspaceContext.state.extensionContext.globalState.update('kitsVersionSaved', kitsVersionCurrent);
13461348
return true;
13471349
} finally {

src/kits/kitsController.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@ export enum KitsReadMode {
3535
allAvailable
3636
}
3737

38+
export interface ScanForKitsControllerOptions {
39+
directoriesToScan?: string[];
40+
removeStaleCompilerKits?: boolean;
41+
}
42+
43+
export function shouldKeepUserKitAfterScan(kit: Kit, discoveredKitNames: ReadonlySet<string>, removeStaleCompilerKits: boolean): boolean {
44+
if (!removeStaleCompilerKits) {
45+
return true;
46+
}
47+
if (!kit.compilers) {
48+
return true;
49+
}
50+
if (kit.keep === true) {
51+
return true;
52+
}
53+
return discoveredKitNames.has(kit.name);
54+
}
55+
3856
// TODO: migrate all kit related things in extension.ts to this class.
3957
export class KitsController {
4058
static additionalCompilerSearchDirs: string[] | undefined;
@@ -229,7 +247,9 @@ export class KitsController {
229247
// We don't have any kits defined. Scan for kits
230248
if (!KitsController.checkingHaveKits) {
231249
KitsController.checkingHaveKits = true;
232-
await KitsController.scanForKits(await this.project.getCMakePathofProject());
250+
await KitsController.scanForKits(await this.project.getCMakePathofProject(), {
251+
removeStaleCompilerKits: this.project.workspaceContext.config.removeStaleKitsOnScan
252+
});
233253
KitsController.checkingHaveKits = false;
234254
return true;
235255
} else {
@@ -289,7 +309,9 @@ export class KitsController {
289309
return false;
290310
} else {
291311
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
292-
await KitsController.scanForKits(await this.project.getCMakePathofProject());
312+
await KitsController.scanForKits(await this.project.getCMakePathofProject(), {
313+
removeStaleCompilerKits: this.project.workspaceContext.config.removeStaleKitsOnScan
314+
});
293315
return false;
294316
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
295317
await KitsController.scanForKitsInSpecificFolder(this.project);
@@ -507,9 +529,12 @@ export class KitsController {
507529
*
508530
* @returns if any duplicate vs kits are removed.
509531
*/
510-
static async scanForKits(cmakePath: string, directoriesToScan?: string[]) {
532+
static async scanForKits(cmakePath: string, options?: ScanForKitsControllerOptions) {
511533
log.debug(localize('rescanning.for.kits', 'Rescanning for kits'));
512534

535+
const directoriesToScan = options?.directoriesToScan;
536+
const removeStaleCompilerKits = options?.removeStaleCompilerKits === true && !directoriesToScan;
537+
513538
// Do the scan:
514539
const discovered_kits = await scanForKits(cmakePath, !!directoriesToScan ? {
515540
scanDirs: directoriesToScan,
@@ -570,7 +595,15 @@ export class KitsController {
570595
old_kits_by_name
571596
);
572597

573-
const new_kits = Object.keys(new_kits_by_name).map(k => new_kits_by_name[k]);
598+
// Build the set of kit names found in this scan so we can drop stale entries.
599+
const discovered_kit_names = new Set(discovered_kits.map(k => k.name));
600+
601+
// Optionally remove compiler-based kits that were not rediscovered in a
602+
// full scan. Partial scans of specific directories never remove existing
603+
// kits because they do not represent the full discovery set.
604+
const new_kits = Object.keys(new_kits_by_name)
605+
.map(k => new_kits_by_name[k])
606+
.filter(kit => shouldKeepUserKitAfterScan(kit, discovered_kit_names, removeStaleCompilerKits));
574607
KitsController.userKits = new_kits;
575608
await KitsController._writeUserKitsFile(cmakePath, new_kits);
576609

@@ -622,10 +655,9 @@ export class KitsController {
622655
}
623656
);
624657

625-
await KitsController.scanForKits(
626-
await project.getCMakePathofProject(),
627-
accumulatedDirs
628-
);
658+
await KitsController.scanForKits(await project.getCMakePathofProject(), {
659+
directoriesToScan: accumulatedDirs
660+
});
629661
}
630662

631663
static isBetterMatch(newKit: Kit, existingKit?: Kit): boolean {

src/presets/presetsController.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,9 @@ export class PresetsController implements vscode.Disposable {
377377
return false;
378378
} else {
379379
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
380-
await KitsController.scanForKits(await this.project.getCMakePathofProject());
380+
await KitsController.scanForKits(await this.project.getCMakePathofProject(), {
381+
removeStaleCompilerKits: this.project.workspaceContext.config.removeStaleKitsOnScan
382+
});
381383
return false;
382384
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
383385
await KitsController.scanForKitsInSpecificFolder(this.project);

0 commit comments

Comments
 (0)