-
Notifications
You must be signed in to change notification settings - Fork 463
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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 | ||
|
@@ -1071,6 +1072,7 @@ export async function findCLCompilerPath(env?: Environment): Promise<string | nu | |
export interface KitScanOptions { | ||
ignorePath?: boolean; | ||
scanDirs?: string[]; | ||
useDefaultScanDirs?: boolean; | ||
} | ||
|
||
/** | ||
|
@@ -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); | ||
|
@@ -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[] = []; | ||
|
@@ -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); | ||
|
@@ -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'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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[]> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 } | ||
]; | ||
|
@@ -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: | ||
|
@@ -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') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
@@ -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) => { | ||
|
@@ -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. | ||
|
@@ -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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
} | ||
|
@@ -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) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm noticing that thsi code is duplicated in the |
||
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; | ||
|
@@ -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; | ||
|
@@ -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: | ||
|
@@ -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; | ||
|
@@ -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; | ||
|
@@ -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> { | ||
|
There was a problem hiding this comment.
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.