diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df2c70e5..f3bb6bc2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -156,6 +156,7 @@ Features: Improvements: - When using CMake presets, the Project Status View now shows the build target along with the build preset. [PR #3241](https://github.com/microsoft/vscode-cmake-tools/pull/3241) - IntelliSense resolves headers coming from MacOS frameworks. CMake 3.27 or later is required. [#2324](https://github.com/microsoft/vscode-cmake-tools/issues/2324) +- Support variable expansion in sourceDirectory setting related to various kit info. [#1914](https://github.com/microsoft/vscode-cmake-tools/issues/1914) - Allow configure settings to override the usual arguments the extension is usually passing to cmake. Don't have unremovable options. [#1639](https://github.com/microsoft/vscode-cmake-tools/issues/1639) - Allow a way to run CTests in parallel by setting `cmake.ctest.allowParallelJobs` to `true`. [#3091](https://github.com/microsoft/vscode-cmake-tools/issues/3091) - When clicking `Run CTests` from the status bar view, it now will bypass the Test Explorer and directly run the CTests. [#3151](https://github.com/microsoft/vscode-cmake-tools/issues/3151) diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts index 74e5a4691..2e27aa5dd 100644 --- a/src/cmakeProject.ts +++ b/src/cmakeProject.ts @@ -924,7 +924,8 @@ export class CMakeProject { } if (selectedFile) { const newSourceDirectory = path.dirname(selectedFile); - await this.setSourceDir(await util.normalizeAndVerifySourceDir(newSourceDirectory, CMakeDriver.sourceDirExpansionOptions(this.workspaceContext.folder.uri.fsPath))); + await this.setSourceDir(await util.normalizeAndVerifySourceDir(newSourceDirectory, + await CMakeDriver.sourceDirExpansionOptions(this.workspaceContext.folder.uri.fsPath, this.useCMakePresets, this.getActiveKit()))); void vscode.workspace.getConfiguration('cmake', this.workspaceFolder.uri).update("sourceDirectory", this._sourceDir); if (config) { // Updating sourceDirectory here, at the beginning of the configure process, @@ -1140,7 +1141,11 @@ export class CMakeProject { */ private async init(sourceDirectory: string) { log.debug(localize('second.phase.init', 'Starting CMake Tools second-phase init')); - await this.setSourceDir(await util.normalizeAndVerifySourceDir(sourceDirectory, CMakeDriver.sourceDirExpansionOptions(this.workspaceContext.folder.uri.fsPath))); + this.kitsController = await KitsController.init(this); + this.presetsController = await PresetsController.init(this, this.kitsController, this.isMultiProjectFolder); + await this.doUseCMakePresetsChange(); + await this.setSourceDir(await util.normalizeAndVerifySourceDir(sourceDirectory, + await CMakeDriver.sourceDirExpansionOptions(this.workspaceContext.folder.uri.fsPath, this.useCMakePresets, this.getActiveKit()))); this.doStatusChange(this.workspaceContext.config.options); // Start up the variant manager await this.variantManager.initialize(this.folderName); @@ -1167,11 +1172,6 @@ export class CMakeProject { this.statusMessage.set(localize('ready.status', 'Ready')); - this.kitsController = await KitsController.init(this); - this.presetsController = await PresetsController.init(this, this.kitsController, this.isMultiProjectFolder); - - await this.doUseCMakePresetsChange(); - this.disposables.push(this.onPresetsChanged(() => this.doUseCMakePresetsChange())); this.disposables.push(this.onUserPresetsChanged(() => this.doUseCMakePresetsChange())); } @@ -1237,6 +1237,27 @@ export class CMakeProject { return this.presetsController.onUserPresetsChanged(listener); } + /** + * Calculate which kit object should be used for variable exapansion. + * Obviously, the active kit if set but otherwise, since variable expansion may need kit information before the active kit is determined + * (like very early during project load) then deduce from previous user selection which was saved in the workspace state. + * Do not change any flow related to how and when this.activeKit is set. When it is null, this method just returns a kit (if we find one) + * but without setting it as active here. + */ + public getActiveKit(): Kit | null { + if (this.activeKit) { + return this.activeKit; + } + + const kitName: string | null = this.workspaceContext.state.getActiveKitName(this.folderName, this.isMultiProjectFolder); + if (kitName) { + // It remembers a kit. Find it in the kits avail in this dir: + return this.kitsController.availableKits.find(k => k.name === kitName) || null; + } + + return null; + } + async initializeKitOrPresets() { if (this.useCMakePresets) { const latestConfigPresetName = this.workspaceContext.state.getConfigurePresetName(this.folderName, this.isMultiProjectFolder); @@ -1250,10 +1271,8 @@ export class CMakeProject { } } else { // Check if the CMakeProject remembers what kit it was last using in this dir: - const kitName = this.workspaceContext.state.getActiveKitName(this.folderName, this.isMultiProjectFolder); - if (kitName) { - // It remembers a kit. Find it in the kits avail in this dir: - const kit = this.kitsController.availableKits.find(k => k.name === kitName) || null; + const kit = this.getActiveKit(); + if (kit) { // Set the kit: (May do nothing if no kit was found) await this.setKit(kit); } diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts index cbdc07923..d112476b8 100644 --- a/src/drivers/cmakeDriver.ts +++ b/src/drivers/cmakeDriver.ts @@ -34,6 +34,7 @@ import { getValue } from '@cmt/preset'; import { CacheEntry } from '@cmt/cache'; import { CMakeBuildRunner } from '@cmt/cmakeBuildRunner'; import { DebuggerInformation } from '@cmt/debug/debuggerConfigureDriver'; +import { getActiveProject } from '@cmt/extension'; import { onBuildSettingsChange, onTestSettingsChange, onPackageSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -422,12 +423,11 @@ export abstract class CMakeDriver implements vscode.Disposable { */ get expansionOptions(): expand.ExpansionOptions { const ws_root = util.lightNormalizePath(this.workspaceFolder || '.'); - const target: Partial = parseTargetTriple(this._kitDetect?.triple ?? '') ?? {}; - const version = this._kitDetect?.version ?? '0.0'; + const target: Partial | undefined = this.useCMakePresets ? undefined : parseTargetTriple(this._kitDetect?.triple ?? '') ?? {}; + const version = this.useCMakePresets ? undefined : this._kitDetect?.version ?? '0.0'; // Fill in default replacements const vars: expand.KitContextVars = { - buildKit: this._kit ? this._kit.name : '__unknownkit__', buildType: this.currentBuildType, generator: this.generatorName || 'null', workspaceFolder: ws_root, @@ -436,14 +436,15 @@ export abstract class CMakeDriver implements vscode.Disposable { workspaceRoot: ws_root, workspaceRootFolderName: path.basename(ws_root), userHome: paths.userHome, - buildKitVendor: this._kitDetect?.vendor ?? '__unknown_vendor__', - buildKitTriple: this._kitDetect?.triple ?? '__unknown_triple__', - buildKitVersion: version, - buildKitHostOs: process.platform, - buildKitTargetOs: target.targetOs ?? '__unknown_target_os__', - buildKitTargetArch: target.targetArch ?? '__unknown_target_arch__', - buildKitVersionMajor: majorVersionSemver(version), - buildKitVersionMinor: minorVersionSemver(version), + buildKit: this.useCMakePresets ? '__kit_not_allowed__' : (this._kit?.name ?? '__unknown_kit__'), + buildKitVendor: this.useCMakePresets ? '__vendor_not_allowed__' : (this._kitDetect?.vendor ?? '__unknown_vendor__'), + buildKitTriple: this.useCMakePresets ? '__triple_not_allowed__' : (this._kitDetect?.triple ?? '__unknown_triple__'), + buildKitVersion: this.useCMakePresets ? '__version_not_allowed__' : (version ?? '__unknown_version__'), + buildKitHostOs: this.useCMakePresets ? '__host_oS_not_allowed__' : process.platform, + buildKitTargetOs: this.useCMakePresets ? '__target_oS_not_allowed__' : (target?.targetOs ?? '__unknown_target_os__'), + buildKitTargetArch: this.useCMakePresets ? '__target_arch_not_allowed__' : (target?.targetArch ?? '__unknown_target_arch__'), + buildKitVersionMajor: this.useCMakePresets ? '__version_major_not_allowed__' : (version ? majorVersionSemver(version) : '__unknown_version_major__'), + buildKitVersionMinor: this.useCMakePresets ? '__version_minor_not_allowed__' : (version ? minorVersionSemver(version) : '__unknown_version_minor__'), sourceDir: this.sourceDir, // DEPRECATED EXPANSION: Remove this in the future: projectName: 'ProjectName' @@ -459,12 +460,34 @@ export abstract class CMakeDriver implements vscode.Disposable { return { vars, variantVars }; } - static sourceDirExpansionOptions(workspaceFolderFspath: string | null): expand.ExpansionOptions { + static async sourceDirExpansionOptions(workspaceFolderFspath: string | null, useCMakePresets: boolean, kit?: Kit | null): Promise { const ws_root = util.lightNormalizePath(workspaceFolderFspath || '.'); + let kitDetect: KitDetect | undefined; + let target: Partial | undefined; + let version: string | undefined; // Fill in default replacements + if (!useCMakePresets) { + if (!kit) { + kit = getActiveProject()?.getActiveKit(); + } + + kitDetect = kit ? await getKitDetect(kit) : undefined; + target = parseTargetTriple(kitDetect?.triple ?? '') ?? {}; + version = kitDetect?.version ?? '0.0'; + } + const vars: expand.MinimalPresetContextVars = { - generator: 'generator', + generator: '__generator_not_allowed__', + buildKit: useCMakePresets ? '__kit_not_allowed__' : (kit?.name ?? '__unknown_kit__'), + buildKitVendor: useCMakePresets ? '__vendor_not_allowed__' : (kitDetect?.vendor ?? '__unknown_vendor__'), + buildKitTriple: useCMakePresets ? '__triple_not_allowed__' : (kitDetect?.triple ?? '__unknown_triple__'), + buildKitVersion: useCMakePresets ? '__version_not_allowed__' : (version ?? '__unknown_version__'), + buildKitHostOs: useCMakePresets ? '__host_os_not_allowed__' : process.platform, + buildKitTargetOs: useCMakePresets ? '__target_os_not_allowed__' : (target?.targetOs ?? '__unknown_target_os__'), + buildKitTargetArch: useCMakePresets ? '__target_arch_not_allowed__' : (target?.targetArch ?? '__unknown_target_arch__'), + buildKitVersionMajor: useCMakePresets ? '__version_major_not_allowed__' : (version ? majorVersionSemver(version) : '__unknown_version_major__'), + buildKitVersionMinor: useCMakePresets ? '__version_minor_not_allowed__' : (version ? minorVersionSemver(version) : '__unknown_version_minor__'), workspaceFolder: ws_root, workspaceFolderBasename: path.basename(ws_root), sourceDir: '${sourceDir}', @@ -798,7 +821,8 @@ export abstract class CMakeDriver implements vscode.Disposable { private async _refreshExpansions(configurePreset?: preset.ConfigurePreset | null) { return this.doRefreshExpansions(async () => { - this.sourceDir = await util.normalizeAndVerifySourceDir(this.sourceDirUnexpanded, CMakeDriver.sourceDirExpansionOptions(this.workspaceFolder)); + this.sourceDir = await util.normalizeAndVerifySourceDir(this.sourceDirUnexpanded, + await CMakeDriver.sourceDirExpansionOptions(this.workspaceFolder, this.useCMakePresets, this._kit)); const opts = this.expansionOptions; opts.envOverride = await this.getConfigureEnvironment(configurePreset); diff --git a/src/expand.ts b/src/expand.ts index 256dd8056..df7f14979 100644 --- a/src/expand.ts +++ b/src/expand.ts @@ -160,7 +160,7 @@ async function expandStringHelper(input: string, opts: ExpansionOptions) { if (key !== 'dollar') { // Replace dollar sign at the very end of the expanding process const replacement = replacements[key]; - if (!replacement) { + if (replacement === undefined) { log.warning(localize('invalid.variable.reference', 'Invalid variable reference {0} in string: {1}', full, input)); } else { subs.set(full, replacement); diff --git a/src/presetsController.ts b/src/presetsController.ts index 8496a9491..4ed8f0fa6 100644 --- a/src/presetsController.ts +++ b/src/presetsController.ts @@ -48,6 +48,15 @@ export class PresetsController { workspaceFolderBasename: path.basename(workspaceFolder), workspaceHash: util.makeHashString(workspaceFolder), workspaceRoot: workspaceFolder, + buildKit: '__kit_not_allowed__', + buildKitVendor: '__vendor_not_allowed__', + buildKitTriple: '__triple_not_allowed__', + buildKitVersion: '__version_not_allowed__', + buildKitHostOs: '__host_os_not_allowed__', + buildKitTargetOs: '__target_os_not_allowed__', + buildKitTargetArch: '__target_arch_not_allowed__', + buildKitVersionMajor: '__version_major_not_allowed__', + buildKitVersionMinor: '__version_minor_not_allowed__', workspaceRootFolderName: path.dirname(workspaceFolder), userHome: paths.userHome, // Following fields are not supported for sourceDir expansion diff --git a/src/projectController.ts b/src/projectController.ts index 027e17842..131d0b2b6 100644 --- a/src/projectController.ts +++ b/src/projectController.ts @@ -239,7 +239,11 @@ export class ProjectController implements vscode.Disposable { } async getProjectForFolder(folder: string): Promise { - const sourceDir = util.platformNormalizePath(await util.normalizeAndVerifySourceDir(folder, CMakeDriver.sourceDirExpansionOptions(folder))); + // If it's too early to have activeProject, assume we use kits. It will get corrected later. + const useCMakePresets: boolean = this.getActiveCMakeProject()?.useCMakePresets || false; + const kit = this.getActiveCMakeProject()?.activeKit; + const sourceDir = util.platformNormalizePath(await util.normalizeAndVerifySourceDir(folder, + await CMakeDriver.sourceDirExpansionOptions(folder, useCMakePresets, kit))); const allCMakeProjects: CMakeProject[] = this.getAllCMakeProjects(); for (const project of allCMakeProjects) { if (util.platformNormalizePath(project.sourceDir) === sourceDir || @@ -397,7 +401,10 @@ export class ProjectController implements vscode.Disposable { } // Normalize the paths. for (let i = 0; i < sourceDirectories.length; i++) { - sourceDirectories[i] = await util.normalizeAndVerifySourceDir(sourceDirectories[i], CMakeDriver.sourceDirExpansionOptions(folder.uri.fsPath)); + // If it's too early to have an activeProject set, assume we use kits. It will get corrected later. + const useCMakePresets: boolean = this.getActiveCMakeProject()?.useCMakePresets || false; + sourceDirectories[i] = await util.normalizeAndVerifySourceDir(sourceDirectories[i], + await CMakeDriver.sourceDirExpansionOptions(folder.uri.fsPath, useCMakePresets, this.getActiveCMakeProject()?.activeKit)); } const projects: CMakeProject[] | undefined = this.getProjectsForWorkspaceFolder(folder);