Skip to content

Commit 078d737

Browse files
feat: scan kits from a folder (#4191)
* feat: scan kits from a folder * updating based on requests in PR review * missed a string --------- Co-authored-by: Garrett Campbell <[email protected]> Co-authored-by: Garrett Campbell <[email protected]>
1 parent 3a877eb commit 078d737

4 files changed

Lines changed: 100 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
Features:
66

77
- Add support for Presets v9, which enables more macro expansion for the `include` field. [#3946](https://github.com/microsoft/vscode-cmake-tools/issues/3946)
8+
- Add support to configure default folder in workspace setting. [#1078](https://github.com/microsoft/vscode-cmake-tools/issues/1078)
89
- Add support to configure default folder in workspace setting. [#1078](https://github.com/microsoft/vscode-cmake-tools/issues/1078) [@sanore](https://github.com/sanore)
910
- Add support for processing LCOV based coverage info files when tests are
1011
executed. This adds test execution type, "Run with coverage", on the `ctest`
1112
section of the Testing tab.
1213
[#4040](https://github.com/microsoft/vscode-cmake-tools/issues/4040)
1314
- Add basic CMake language services: quick hover and completions for CMake built-ins. [PR #4204](https://github.com/microsoft/vscode-cmake-tools/pull/4204)
15+
- Add an option to scan kits from a folder. [#4191](https://github.com/microsoft/vscode-cmake-tools/pull/4191) [@std-microblock](https://github.com/std-microblock)
1416

1517
Improvements:
1618

src/kits/kit.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const log = logging.createLogger('kit');
3434
*/
3535
export enum SpecialKits {
3636
ScanForKits = '__scanforkits__',
37-
Unspecified = '__unspec__'
37+
Unspecified = '__unspec__',
38+
ScanSpecificDir = '__scan_specific_dir__',
3839
}
3940
export const SpecialKitsCount: number = 2;
4041
export type UnspecifiedKit = SpecialKits.Unspecified;
@@ -1030,7 +1031,7 @@ export async function effectiveKitEnvironment(kit: Kit, opts?: expand.ExpansionO
10301031
} else {
10311032
const vs_vars = await getVSKitEnvironment(kit);
10321033
env = EnvironmentUtils.merge([env, vs_vars]);
1033-
const expandOptions: expand.ExpansionOptions = opts ? {...opts, envOverride: env, penvOverride: env } : {
1034+
const expandOptions: expand.ExpansionOptions = opts ? { ...opts, envOverride: env, penvOverride: env } : {
10341035
vars: {} as expand.KitContextVars,
10351036
envOverride: env,
10361037
penvOverride: env
@@ -1071,6 +1072,7 @@ export async function findCLCompilerPath(env?: Environment): Promise<string | nu
10711072
export interface KitScanOptions {
10721073
ignorePath?: boolean;
10731074
scanDirs?: string[];
1075+
useDefaultScanDirs?: boolean;
10741076
}
10751077

10761078
/**
@@ -1094,6 +1096,9 @@ export async function scanForKits(cmakePath?: string, opt?: KitScanOptions) {
10941096
// Maps paths to booleans indicating if the path is trusted.
10951097
const scan_paths = new Map<string, boolean>();
10961098
function addScanPath(path: string, trusted: boolean, paths?: Map<string, boolean>) {
1099+
if (opt?.useDefaultScanDirs === false && !opt.scanDirs?.includes(path)) {
1100+
return;
1101+
}
10971102
const normalizedPath = util.lightNormalizePath(path);
10981103
const map = paths ?? scan_paths;
10991104
map.set(normalizedPath, map.get(normalizedPath) || trusted);
@@ -1140,6 +1145,12 @@ export async function scanForKits(cmakePath?: string, opt?: KitScanOptions) {
11401145

11411146
// PATH environment variable locations
11421147
scan_paths.forEach((isTrusted, path) => addScanPath(path, isTrusted, clang_paths));
1148+
1149+
// Paths provided by the user
1150+
for (const path of opt?.scanDirs ?? []) {
1151+
addScanPath(path, true, clang_paths);
1152+
}
1153+
11431154
// LLVM bundled in VS locations
11441155
const vs_installs = await vsInstallations();
11451156
const bundled_clang_paths: string[] = [];
@@ -1251,7 +1262,14 @@ export async function descriptionForKit(kit: Kit, shortVsName: boolean = false):
12511262
if (kit.name === SpecialKits.ScanForKits) {
12521263
return localize('search.for.compilers', 'Search for compilers on this computer');
12531264
}
1254-
return localize('unspecified.let.cmake.guess', 'Unspecified (Let CMake guess what compilers and environment to use)');
1265+
if (kit.name === SpecialKits.Unspecified) {
1266+
return localize('unspecified.let.cmake.guess', 'Unspecified (Let CMake guess what compilers and environment to use)');
1267+
}
1268+
if (kit.name === SpecialKits.ScanSpecificDir) {
1269+
return localize('search.for.compilers.in.dir', 'Search recursively for compilers in a specific directory (max depth: 5)');
1270+
}
1271+
1272+
return '';
12551273
}
12561274

12571275
export async function readKitsFile(filePath: string, workspaceFolder?: string, expansionOptions?: expand.ExpansionOptions): Promise<Kit[]> {

src/kits/kitsController.ts

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ export class KitsController {
119119
KitsController.specialKits = [
120120
// Spcial __scanforkits__ kit used for invoking the "Scan for kits"
121121
{ name: SpecialKits.ScanForKits, isTrusted: true },
122+
// Special __scanSpecificDir__ kit for invoking the "Scan for kits in specific directories"
123+
{ name: SpecialKits.ScanSpecificDir, isTrusted: true },
122124
// Special __unspec__ kit for opting-out of kits
123125
{ name: SpecialKits.Unspecified, isTrusted: true }
124126
];
@@ -249,6 +251,8 @@ export class KitsController {
249251
switch (kit.name) {
250252
case SpecialKits.ScanForKits as string:
251253
return `[${localize('scan.for.kits.button', 'Scan for kits')}]`;
254+
case SpecialKits.ScanSpecificDir as string:
255+
return `[${localize('scan.for.kits.in.specific.dir.button', 'Scan recursively for kits in specific directories (max depth: 5)')}]`;
252256
case SpecialKits.Unspecified as string:
253257
return `[${localize('unspecified.kit.name', 'Unspecified')}]`;
254258
default:
@@ -276,6 +280,9 @@ export class KitsController {
276280
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
277281
await KitsController.scanForKits(await this.project.getCMakePathofProject());
278282
return false;
283+
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
284+
await KitsController.scanForKitsInSpecificFolder(this.project);
285+
return false;
279286
} else {
280287
log.debug(localize('user.selected.kit', 'User selected kit {0}', JSON.stringify(chosen_kit)));
281288
const kitChanged = chosen_kit.kit !== this.project.activeKit;
@@ -423,7 +430,7 @@ export class KitsController {
423430

424431
// Remove the special kits
425432
const stripped_kits = kits.filter(kit => ((kit.name !== SpecialKits.ScanForKits) &&
426-
(kit.name !== SpecialKits.Unspecified)));
433+
(kit.name !== SpecialKits.Unspecified) && (kit.name !== SpecialKits.ScanSpecificDir)));
427434

428435
// Sort the kits by name so they always appear in order in the file.
429436
const sorted_kits = stripped_kits.sort((a, b) => {
@@ -489,11 +496,14 @@ export class KitsController {
489496
*
490497
* @returns if any duplicate vs kits are removed.
491498
*/
492-
static async scanForKits(cmakePath: string) {
499+
static async scanForKits(cmakePath: string, directoriesToScan?: string[]) {
493500
log.debug(localize('rescanning.for.kits', 'Rescanning for kits'));
494501

495502
// Do the scan:
496-
const discovered_kits = await scanForKits(cmakePath, { scanDirs: KitsController.additionalCompilerSearchDirs });
503+
const discovered_kits = await scanForKits(cmakePath, !!directoriesToScan ? {
504+
scanDirs: directoriesToScan,
505+
useDefaultScanDirs: false
506+
} : { scanDirs: KitsController.additionalCompilerSearchDirs });
497507

498508
// The list with the new definition user kits starts with the non VS ones,
499509
// which do not have any variations in the way they can be defined.
@@ -558,6 +568,55 @@ export class KitsController {
558568
return duplicateRemoved;
559569
}
560570

571+
static async scanForKitsInSpecificFolder(project: CMakeProject) {
572+
const dir = await vscode.window.showOpenDialog({
573+
canSelectFiles: false,
574+
canSelectFolders: true,
575+
canSelectMany: false,
576+
openLabel: localize('select.folder', 'Select Folder')
577+
});
578+
const dirPathWithDepth = async (folder: string, progress: ProgressHandle, cancel: vscode.CancellationToken, depth: number = 5): Promise<string[]> => {
579+
if (cancel.isCancellationRequested) {
580+
return [];
581+
}
582+
583+
const dir = await fs.readdir(folder);
584+
const files: string[] = [];
585+
progress.report({ message: folder });
586+
for (const file of dir) {
587+
const filePath = path.join(folder, file);
588+
if (depth > 0 && (await fs.stat(filePath)).isDirectory()) {
589+
files.push(...await dirPathWithDepth(filePath, progress, cancel, depth - 1));
590+
files.push(filePath);
591+
}
592+
}
593+
594+
return files;
595+
};
596+
597+
if (!dir || dir.length === 0) {
598+
return;
599+
}
600+
601+
const accumulatedDirs: string[] = [];
602+
await vscode.window.withProgress(
603+
{
604+
location: vscode.ProgressLocation.Notification,
605+
title: localize('accumulating.folders', 'Recursively accumulating folders to scan'),
606+
cancellable: true
607+
},
608+
async (progress, cancel) => {
609+
accumulatedDirs.push(dir[0].fsPath);
610+
accumulatedDirs.push(...(await dirPathWithDepth(dir[0].fsPath, progress, cancel)));
611+
}
612+
);
613+
614+
await KitsController.scanForKits(
615+
await project.getCMakePathofProject(),
616+
accumulatedDirs
617+
);
618+
}
619+
561620
static isBetterMatch(newKit: Kit, existingKit?: Kit): boolean {
562621
if (KitsController.isBetterClangCLDefinition(newKit, existingKit)) {
563622
return true;
@@ -574,7 +633,7 @@ export class KitsController {
574633
return false;
575634
}
576635

577-
static isBetterCompilerMatch(newCompilers?: {[lang: string]: string}, existingCompilers?: {[lang: string]: string}): boolean {
636+
static isBetterCompilerMatch(newCompilers?: { [lang: string]: string }, existingCompilers?: { [lang: string]: string }): boolean {
578637
// Try to keep the best match (e.g. compilers for C and CXX exist)
579638
if (!existingCompilers) {
580639
return true;

src/presets/presetsController.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ export class PresetsController implements vscode.Disposable {
282282
} else if (kit.visualStudio && !kit.compilers) {
283283
const hostTargetArch = getHostTargetArchString(kit.visualStudioArchitecture!, kit.preferredGenerator?.platform);
284284
return `${(kit.preferredGenerator?.name || 'Visual Studio')} ${hostTargetArch}`;
285+
} else if (kit.name === SpecialKits.ScanSpecificDir) {
286+
return `[${localize('scan.for.compilers.in.dir', 'Scan recursively for compilers in directory (max depth: 5)')}]`;
285287
} else {
286288
return kit.name;
287289
}
@@ -304,6 +306,9 @@ export class PresetsController implements vscode.Disposable {
304306
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
305307
await KitsController.scanForKits(await this.project.getCMakePathofProject());
306308
return false;
309+
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
310+
await KitsController.scanForKitsInSpecificFolder(this.project);
311+
return false;
307312
} else {
308313
log.debug(localize('user.selected.compiler', 'User selected compiler {0}', JSON.stringify(chosen_kit)));
309314
const generator = chosen_kit.kit.preferredGenerator?.name;
@@ -709,8 +714,10 @@ export class PresetsController implements vscode.Disposable {
709714
const presets = preset.allConfigurePresets(this.folderPath);
710715
const configurePreset = await this.selectNonHiddenPreset(presets, presets, { placeHolder });
711716
if (configurePreset) {
712-
newPreset = { name: '__placeholder__', description: '', displayName: '',
713-
steps: [{type: "configure", name: configurePreset}] };
717+
newPreset = {
718+
name: '__placeholder__', description: '', displayName: '',
719+
steps: [{ type: "configure", name: configurePreset }]
720+
};
714721
}
715722

716723
break;
@@ -720,11 +727,11 @@ export class PresetsController implements vscode.Disposable {
720727
const presets = preset.allWorkflowPresets(this.folderPath);
721728
const workflowBasePresetName = await this.selectNonHiddenPreset(presets, presets, { placeHolder, canPickMany: false });
722729
const workflowBasePreset = presets.find(pr => pr.name === workflowBasePresetName);
723-
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: workflowBasePreset?.steps || [{type: "configure", name: "_placeholder_"}] };
730+
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: workflowBasePreset?.steps || [{ type: "configure", name: "_placeholder_" }] };
724731
break;
725732
}
726733
case SpecialOptions.Custom: {
727-
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: [{type: "configure", name: "_placeholder_"}] };
734+
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: [{ type: "configure", name: "_placeholder_" }] };
728735
break;
729736
}
730737
default:
@@ -842,7 +849,7 @@ export class PresetsController implements vscode.Disposable {
842849
await this.setConfigurePreset(chosenPreset);
843850
}
844851

845-
if (this.project.workspaceContext.config.automaticReconfigure && !quickStart) {
852+
if (this.project.workspaceContext.config.automaticReconfigure && !quickStart) {
846853
await this.project.configureInternal(ConfigureTrigger.selectConfigurePreset, [], ConfigureType.Normal);
847854
}
848855
return !addPreset || allPresets.length === 0;
@@ -1150,8 +1157,7 @@ export class PresetsController implements vscode.Disposable {
11501157
}
11511158
}
11521159

1153-
private checkCompatibility(configurePreset: preset.ConfigurePreset | null, buildPreset?: preset.BuildPreset | null, testPreset?: preset.TestPreset | null, packagePreset?: preset.PackagePreset | null, workflowPreset?: preset.WorkflowPreset | null):
1154-
{buildPresetCompatible: boolean; testPresetCompatible: boolean; packagePresetCompatible: boolean; workflowPresetCompatible: boolean} {
1160+
private checkCompatibility(configurePreset: preset.ConfigurePreset | null, buildPreset?: preset.BuildPreset | null, testPreset?: preset.TestPreset | null, packagePreset?: preset.PackagePreset | null, workflowPreset?: preset.WorkflowPreset | null): { buildPresetCompatible: boolean; testPresetCompatible: boolean; packagePresetCompatible: boolean; workflowPresetCompatible: boolean } {
11551161
let testPresetCompatible = true;
11561162
let buildPresetCompatible = true;
11571163
let packagePresetCompatible = true;
@@ -1215,7 +1221,7 @@ export class PresetsController implements vscode.Disposable {
12151221
workflowPresetCompatible = (temp === undefined);
12161222
}
12171223

1218-
return {buildPresetCompatible, testPresetCompatible, packagePresetCompatible, workflowPresetCompatible};
1224+
return { buildPresetCompatible, testPresetCompatible, packagePresetCompatible, workflowPresetCompatible };
12191225
}
12201226

12211227
async selectTestPreset(): Promise<boolean> {

0 commit comments

Comments
 (0)