Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scan kits from a folder #4191

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Features:

- Add support for Presets v9, which enables more macro expansion for the `include` field. [#3946](https://github.com/microsoft/vscode-cmake-tools/issues/3946)
- Add support to configure default folder in workspace setting. [#1078](https://github.com/microsoft/vscode-cmake-tools/issues/1078)
- Add the scan kits from a folder option
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put a link to this PR following the pattern from other entries.


Improvements:

Expand Down
40 changes: 29 additions & 11 deletions src/kits/kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const log = logging.createLogger('kit');
*/
export enum SpecialKits {
ScanForKits = '__scanforkits__',
Unspecified = '__unspec__'
Unspecified = '__unspec__',
ScanSpecificDir = '__scan_specific_dir__',
}
export const SpecialKitsCount: number = 2;
export type UnspecifiedKit = SpecialKits.Unspecified;
Expand Down Expand Up @@ -1030,7 +1031,7 @@ export async function effectiveKitEnvironment(kit: Kit, opts?: expand.ExpansionO
} else {
const vs_vars = await getVSKitEnvironment(kit);
env = EnvironmentUtils.merge([env, vs_vars]);
const expandOptions: expand.ExpansionOptions = opts ? {...opts, envOverride: env, penvOverride: env } : {
const expandOptions: expand.ExpansionOptions = opts ? { ...opts, envOverride: env, penvOverride: env } : {
vars: {} as expand.KitContextVars,
envOverride: env,
penvOverride: env
Expand Down Expand Up @@ -1071,6 +1072,7 @@ export async function findCLCompilerPath(env?: Environment): Promise<string | nu
export interface KitScanOptions {
ignorePath?: boolean;
scanDirs?: string[];
useDefaultScanDirs?: boolean;
}

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

// PATH environment variable locations
scan_paths.forEach((isTrusted, path) => addScanPath(path, isTrusted, clang_paths));

// Paths provided by the user
for (const path of opt?.scanDirs ?? []) {
addScanPath(path, true, clang_paths);
}

// LLVM bundled in VS locations
const vs_installs = await vsInstallations();
const bundled_clang_paths: string[] = [];
Expand Down Expand Up @@ -1182,14 +1193,14 @@ export async function scanForKits(cmakePath?: string, opt?: KitScanOptions) {
Array.from(untrusted_paths).toString()),
{ action: 'yes', title: localize('yes', 'Yes') },
{ action: 'no', title: localize('no', 'No') }).then(async action => {
if (action?.action === 'yes') {
const settings = vscode.workspace.getConfiguration('cmake');
const additionalCompilerSearchDirs = settings.get<string[]>('additionalCompilerSearchDirs', []);
additionalCompilerSearchDirs.push(...Array.from(untrusted_paths));
await settings.update('additionalCompilerSearchDirs', additionalCompilerSearchDirs, vscode.ConfigurationTarget.Global);
await vscode.commands.executeCommand('cmake.scanForKits');
}
});
if (action?.action === 'yes') {
const settings = vscode.workspace.getConfiguration('cmake');
const additionalCompilerSearchDirs = settings.get<string[]>('additionalCompilerSearchDirs', []);
additionalCompilerSearchDirs.push(...Array.from(untrusted_paths));
await settings.update('additionalCompilerSearchDirs', additionalCompilerSearchDirs, vscode.ConfigurationTarget.Global);
await vscode.commands.executeCommand('cmake.scanForKits');
}
});
}

return result.filter(kit => kit.isTrusted);
Expand Down Expand Up @@ -1251,7 +1262,14 @@ export async function descriptionForKit(kit: Kit, shortVsName: boolean = false):
if (kit.name === SpecialKits.ScanForKits) {
return localize('search.for.compilers', 'Search for compilers on this computer');
}
return localize('unspecified.let.cmake.guess', 'Unspecified (Let CMake guess what compilers and environment to use)');
if (kit.name === SpecialKits.Unspecified) {
return localize('unspecified.let.cmake.guess', 'Unspecified (Let CMake guess what compilers and environment to use)');
}
if (kit.name === SpecialKits.ScanSpecificDir) {
return localize('search.for.compilers.in.dir', 'Search for compilers in a specific directory');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should likely make note here or surface to the user that it will recursively search, if that's what you'd like to happen.

}

return '';
}

export async function readKitsFile(filePath: string, workspaceFolder?: string, expansionOptions?: expand.ExpansionOptions): Promise<Kit[]> {
Expand Down
41 changes: 37 additions & 4 deletions src/kits/kitsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ export class KitsController {
KitsController.specialKits = [
// Spcial __scanforkits__ kit used for invoking the "Scan for kits"
{ name: SpecialKits.ScanForKits, isTrusted: true },
// Special __scanSpecificDir__ kit for invoking the "Scan for kits in specific directories"
{ name: SpecialKits.ScanSpecificDir, isTrusted: true },
// Special __unspec__ kit for opting-out of kits
{ name: SpecialKits.Unspecified, isTrusted: true }
];
Expand Down Expand Up @@ -249,6 +251,8 @@ export class KitsController {
switch (kit.name) {
case SpecialKits.ScanForKits as string:
return `[${localize('scan.for.kits.button', 'Scan for kits')}]`;
case SpecialKits.ScanSpecificDir as string:
return `[${localize('scan.for.kits.in.specific.dir.button', 'Scan for kits in specific directories')}]`;
case SpecialKits.Unspecified as string:
return `[${localize('unspecified.kit.name', 'Unspecified')}]`;
default:
Expand Down Expand Up @@ -276,6 +280,32 @@ export class KitsController {
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
await KitsController.scanForKits(await this.project.getCMakePathofProject());
return false;
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
const dir = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: localize('select.folder', 'Select Folder')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think it may be a good idea to ensure somewhere that it will also search recursively within the folder. Either here or in the popup that asks to either scan for kits or choose a specific folder.

});
if (!dir || dir.length === 0) {
return false;
}
const dirPathWithDepth = async (folder: string, depth: number = 5) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming my understanding, this is a recursive method adding ALL directories under the selected folder to the paths to search?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, It has a depth limit of 5.

const dir = await fs.readdir(folder);
const files: string[] = [];
for (const file of dir) {
const filePath = path.join(folder, file);
if (depth > 0 && (await fs.stat(filePath)).isDirectory()) {
files.push(...await dirPathWithDepth(filePath, depth - 1));
files.push(filePath);
}
}

return files;
};

await KitsController.scanForKits(await this.project.getCMakePathofProject(), [dir[0].fsPath, ...(await dirPathWithDepth(dir[0].fsPath))]);
return false;
} else {
log.debug(localize('user.selected.kit', 'User selected kit {0}', JSON.stringify(chosen_kit)));
const kitChanged = chosen_kit.kit !== this.project.activeKit;
Expand Down Expand Up @@ -423,7 +453,7 @@ export class KitsController {

// Remove the special kits
const stripped_kits = kits.filter(kit => ((kit.name !== SpecialKits.ScanForKits) &&
(kit.name !== SpecialKits.Unspecified)));
(kit.name !== SpecialKits.Unspecified) && (kit.name !== SpecialKits.ScanSpecificDir)));

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

// Do the scan:
const discovered_kits = await scanForKits(cmakePath, { scanDirs: KitsController.additionalCompilerSearchDirs });
const discovered_kits = await scanForKits(cmakePath, !!directoriesToScan ? {
scanDirs: directoriesToScan,
useDefaultScanDirs: false
} : { scanDirs: KitsController.additionalCompilerSearchDirs });

// The list with the new definition user kits starts with the non VS ones,
// which do not have any variations in the way they can be defined.
Expand Down Expand Up @@ -574,7 +607,7 @@ export class KitsController {
return false;
}

static isBetterCompilerMatch(newCompilers?: {[lang: string]: string}, existingCompilers?: {[lang: string]: string}): boolean {
static isBetterCompilerMatch(newCompilers?: { [lang: string]: string }, existingCompilers?: { [lang: string]: string }): boolean {
// Try to keep the best match (e.g. compilers for C and CXX exist)
if (!existingCompilers) {
return true;
Expand Down
45 changes: 37 additions & 8 deletions src/presets/presetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ export class PresetsController implements vscode.Disposable {
} else if (kit.visualStudio && !kit.compilers) {
const hostTargetArch = getHostTargetArchString(kit.visualStudioArchitecture!, kit.preferredGenerator?.platform);
return `${(kit.preferredGenerator?.name || 'Visual Studio')} ${hostTargetArch}`;
} else if (kit.name === SpecialKits.ScanSpecificDir) {
return `[${localize('scan.for.compilers.in.dir', 'Scan for compilers in directory')}]`;
} else {
return kit.name;
}
Expand All @@ -304,6 +306,32 @@ export class PresetsController implements vscode.Disposable {
if (chosen_kit.kit.name === SpecialKits.ScanForKits) {
await KitsController.scanForKits(await this.project.getCMakePathofProject());
return false;
} else if (chosen_kit.kit.name === SpecialKits.ScanSpecificDir) {
const dir = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: localize('select.folder', 'Select Folder')
});
if (!dir || dir.length === 0) {
return false;
}
const dirPathWithDepth = async (folder: string, depth: number = 5) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm noticing that thsi code is duplicated in the kitsController.ts and the presetsController.ts. Please extract it into a method so that we share code. Likely the best place to put it is in the kitsController alongside scanForKits or in kit.ts.

const dir = await fs.readdir(folder);
const files: string[] = [];
for (const file of dir) {
const filePath = path.join(folder, file);
if (depth > 0 && (await fs.stat(filePath)).isDirectory()) {
files.push(...await dirPathWithDepth(filePath, depth - 1));
files.push(filePath);
}
}

return files;
};

await KitsController.scanForKits(await this.project.getCMakePathofProject(), [dir[0].fsPath, ...(await dirPathWithDepth(dir[0].fsPath))]);
return false;
} else {
log.debug(localize('user.selected.compiler', 'User selected compiler {0}', JSON.stringify(chosen_kit)));
const generator = chosen_kit.kit.preferredGenerator?.name;
Expand Down Expand Up @@ -709,8 +737,10 @@ export class PresetsController implements vscode.Disposable {
const presets = preset.allConfigurePresets(this.folderPath);
const configurePreset = await this.selectNonHiddenPreset(presets, presets, { placeHolder });
if (configurePreset) {
newPreset = { name: '__placeholder__', description: '', displayName: '',
steps: [{type: "configure", name: configurePreset}] };
newPreset = {
name: '__placeholder__', description: '', displayName: '',
steps: [{ type: "configure", name: configurePreset }]
};
}

break;
Expand All @@ -720,11 +750,11 @@ export class PresetsController implements vscode.Disposable {
const presets = preset.allWorkflowPresets(this.folderPath);
const workflowBasePresetName = await this.selectNonHiddenPreset(presets, presets, { placeHolder, canPickMany: false });
const workflowBasePreset = presets.find(pr => pr.name === workflowBasePresetName);
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: workflowBasePreset?.steps || [{type: "configure", name: "_placeholder_"}] };
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: workflowBasePreset?.steps || [{ type: "configure", name: "_placeholder_" }] };
break;
}
case SpecialOptions.Custom: {
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: [{type: "configure", name: "_placeholder_"}] };
newPreset = { name: '__placeholder__', description: '', displayName: '', steps: [{ type: "configure", name: "_placeholder_" }] };
break;
}
default:
Expand Down Expand Up @@ -842,7 +872,7 @@ export class PresetsController implements vscode.Disposable {
await this.setConfigurePreset(chosenPreset);
}

if (this.project.workspaceContext.config.automaticReconfigure && !quickStart) {
if (this.project.workspaceContext.config.automaticReconfigure && !quickStart) {
await this.project.configureInternal(ConfigureTrigger.selectConfigurePreset, [], ConfigureType.Normal);
}
return !addPreset || allPresets.length === 0;
Expand Down Expand Up @@ -1150,8 +1180,7 @@ export class PresetsController implements vscode.Disposable {
}
}

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} {
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 } {
let testPresetCompatible = true;
let buildPresetCompatible = true;
let packagePresetCompatible = true;
Expand Down Expand Up @@ -1215,7 +1244,7 @@ export class PresetsController implements vscode.Disposable {
workflowPresetCompatible = (temp === undefined);
}

return {buildPresetCompatible, testPresetCompatible, packagePresetCompatible, workflowPresetCompatible};
return { buildPresetCompatible, testPresetCompatible, packagePresetCompatible, workflowPresetCompatible };
}

async selectTestPreset(): Promise<boolean> {
Expand Down