Skip to content

Commit f35be38

Browse files
Correctly store cache and update binaryDir (#4028)
* fix binary dir * fixes the issue, but more testing and possible improvement to come * set expandedPreset.binaryDir if already set too * properly update caches, ensure we correctly store cached presets * remove some unnecessary comments * fix linter errors * update envOverride with expandedPreset env * update the changelog * update parent environment when never
1 parent 5fbcb13 commit f35be38

File tree

4 files changed

+90
-52
lines changed

4 files changed

+90
-52
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Bug Fixes:
66

77
- Fix env expansion of all variables (toolchainFile, etc.) in presets. [#4019](https://github.com/microsoft/vscode-cmake-tools/issues/4019)
8+
- Fix issues with inheritance and presets. [#4023](https://github.com/microsoft/vscode-cmake-tools/issues/4023)
89
- Fix generator and preferredGenerator logic. [#4031](https://github.com/microsoft/vscode-cmake-tools/issues/4031), [#4005](https://github.com/microsoft/vscode-cmake-tools/issues/4005), [#4032](https://github.com/microsoft/vscode-cmake-tools/issues/4032)
910

1011
## 1.19.49

src/cmakeProject.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ export class CMakeProject {
311311
}
312312
}
313313

314-
preset.cacheExpandedPreset(this.folderPath, expandedConfigurePreset, "configurePresets");
314+
preset.updateCachedExpandedPreset(this.folderPath, expandedConfigurePreset, "configurePresets");
315315

316316
// Make sure we pass CMakeDriver the preset defined env as well as the parent env
317317
expandedConfigurePreset.environment = EnvironmentUtils.mergePreserveNull([expandedConfigurePreset.__parentEnvironment, expandedConfigurePreset.environment]);
@@ -407,7 +407,7 @@ export class CMakeProject {
407407
}
408408

409409
if (expandedBuildPreset.name !== preset.defaultBuildPreset.name) {
410-
preset.cacheExpandedPreset(this.folderPath, expandedBuildPreset, "buildPresets");
410+
preset.updateCachedExpandedPreset(this.folderPath, expandedBuildPreset, "buildPresets");
411411
}
412412

413413
// Make sure we pass CMakeDriver the preset defined env as well as the parent env
@@ -501,7 +501,7 @@ export class CMakeProject {
501501
}
502502

503503
if (expandedTestPreset.name !== preset.defaultTestPreset.name) {
504-
preset.cacheExpandedPreset(this.folderPath, expandedTestPreset, "testPresets");
504+
preset.updateCachedExpandedPreset(this.folderPath, expandedTestPreset, "testPresets");
505505
}
506506

507507
// Make sure we pass CMakeDriver the preset defined env as well as the parent env
@@ -595,7 +595,7 @@ export class CMakeProject {
595595
}
596596

597597
if (expandedPackagePreset.name !== preset.defaultPackagePreset.name) {
598-
preset.cacheExpandedPreset(this.folderPath, expandedPackagePreset, "packagePresets");
598+
preset.updateCachedExpandedPreset(this.folderPath, expandedPackagePreset, "packagePresets");
599599
}
600600

601601
// Make sure we pass CMakeDriver the preset defined env as well as the parent env

src/preset.ts

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -492,26 +492,54 @@ export function setExpandedPresets(folder: string, presets: PresetsFile | undefi
492492
expandedPresets.set(folder, presets);
493493
}
494494

495-
export function cacheExpandedPreset(folder: string, preset: Preset, presetType: 'configurePresets' | 'buildPresets' | 'testPresets' | 'packagePresets' | 'workflowPresets') {
496-
const expanded = expandedPresets.get(folder);
495+
/**
496+
* This function updates the cache in both the regular presets cache and user presets cache.
497+
* However, this only updates the cache if the preset was already in the cache.
498+
* @param folder Folder to grab the cached expanded presets for
499+
* @param preset The updated preset to cache
500+
* @param presetType Type of the preset.
501+
*/
502+
export function updateCachedExpandedPreset(folder: string, preset: Preset, presetType: 'configurePresets' | 'buildPresets' | 'testPresets' | 'packagePresets' | 'workflowPresets') {
497503
const clonedPreset = lodash.cloneDeep(preset);
498-
if (expanded) {
499-
if (presetType === 'configurePresets') {
500-
expanded.configurePresets = expanded.configurePresets?.filter(p => p.name !== clonedPreset.name);
501-
expanded.configurePresets?.push(clonedPreset as ConfigurePreset);
502-
} else if (presetType === "buildPresets") {
503-
expanded.buildPresets = expanded.buildPresets?.filter(p => p.name !== clonedPreset.name);
504-
expanded.buildPresets?.push(clonedPreset as BuildPreset);
505-
} else if (presetType === "testPresets") {
506-
expanded.testPresets = expanded.testPresets?.filter(p => p.name !== clonedPreset.name);
507-
expanded.testPresets?.push(clonedPreset as TestPreset);
508-
} else if (presetType === "packagePresets") {
509-
expanded.packagePresets = expanded.packagePresets?.filter(p => p.name !== clonedPreset.name);
510-
expanded.packagePresets?.push(clonedPreset as PackagePreset);
511-
} else if (presetType === "workflowPresets") {
512-
expanded.workflowPresets = expanded.workflowPresets?.filter(p => p.name !== clonedPreset.name);
513-
expanded.workflowPresets?.push(clonedPreset as WorkflowPreset);
514-
}
504+
const expanded = expandedPresets.get(folder);
505+
const userExpanded = expandedUserPresets.get(folder);
506+
updateCachedExpandedPresethelper(expanded, clonedPreset, presetType);
507+
updateCachedExpandedPresethelper(userExpanded, clonedPreset, presetType);
508+
}
509+
510+
/**
511+
* Updates the cache only if the preset was already present in the cache.
512+
* Updates the cache in-place, the sorting of the list will remain the same.
513+
* @param cache The cache to update.
514+
* @param preset The updated preset to cache
515+
* @param presetType Type of the preset.
516+
* @returns void
517+
*/
518+
function updateCachedExpandedPresethelper(cache: PresetsFile | undefined, preset: Preset, presetType: 'configurePresets' | 'buildPresets' | 'testPresets' | 'packagePresets' | 'workflowPresets') {
519+
// Exit early if the cache or the list of presets is undefined.
520+
if (!cache || !cache[presetType]) {
521+
return;
522+
}
523+
524+
// Exit early if the cache doesn't contain the preset.
525+
const index = cache[presetType]!.findIndex(p => p.name === preset.name);
526+
if (index === -1) {
527+
return;
528+
}
529+
530+
// TODO: I'd like to try and figure out how to template this so that we don't have this logic duplicated for each if statement.
531+
// We know that the list exists so we use "!".
532+
// We use slice so that we can insert the updated preset in the same location it was previously in.
533+
if (presetType === 'configurePresets') {
534+
cache.configurePresets = [...cache.configurePresets!.slice(0, index), preset as ConfigurePreset, ...cache.configurePresets!.slice(index + 1)];
535+
} else if (presetType === "buildPresets") {
536+
cache.buildPresets = [...cache.buildPresets!.slice(0, index), preset as BuildPreset, ...cache.buildPresets!.slice(index + 1)];
537+
} else if (presetType === "testPresets") {
538+
cache.testPresets = [...cache.testPresets!.slice(0, index), preset as TestPreset, ...cache.testPresets!.slice(index + 1)];
539+
} else if (presetType === "packagePresets") {
540+
cache.packagePresets = [...cache.packagePresets!.slice(0, index), preset as PackagePreset, ...cache.packagePresets!.slice(index + 1)];
541+
} else if (presetType === "workflowPresets") {
542+
cache.workflowPresets = [...cache.workflowPresets!.slice(0, index), preset as WorkflowPreset, ...cache.workflowPresets!.slice(index + 1)];
515543
}
516544
}
517545

@@ -1056,6 +1084,7 @@ async function getVsDevEnv(opts: VsDevEnvOptions): Promise<EnvironmentWithNull |
10561084
export async function tryApplyVsDevEnv(preset: ConfigurePreset, workspaceFolder: string, sourceDir: string): Promise<void> {
10571085
const useVsDeveloperEnvironmentMode = vscode.workspace.getConfiguration("cmake", vscode.Uri.file(workspaceFolder)).get("useVsDeveloperEnvironment") as UseVsDeveloperEnvironment;
10581086
if (useVsDeveloperEnvironmentMode === "never") {
1087+
preset.__parentEnvironment = process.env;
10591088
return;
10601089
}
10611090

@@ -1187,6 +1216,7 @@ async function getConfigurePresetInheritsImpl(folder: string, name: string, allo
11871216
return null;
11881217
}
11891218

1219+
// This function modifies the preset parameter in-place. This means that the cache will be updated if the preset was retreived from the cache and not cloned.
11901220
async function getConfigurePresetInheritsHelper(folder: string, preset: ConfigurePreset, allowUserPreset: boolean = false, usePresetsPlusIncluded: boolean = false, errorHandler?: ExpansionErrorHandler) {
11911221
if (preset.__expanded) {
11921222
return preset;
@@ -1262,6 +1292,7 @@ async function getConfigurePresetInheritsHelper(folder: string, preset: Configur
12621292
return preset;
12631293
}
12641294

1295+
// This function does not modify the preset in place, it constructs a new expanded preset and returns it.
12651296
export async function expandConfigurePresetVariables(preset: ConfigurePreset, folder: string, name: string, workspaceFolder: string, sourceDir: string, allowUserPreset: boolean = false, usePresetsPlusIncluded: boolean = false, errorHandler?: ExpansionErrorHandler): Promise<ConfigurePreset> {
12661297

12671298
// Put the preset.environment on top of combined environment in the `__parentEnvironment` field.
@@ -1286,19 +1317,22 @@ export async function expandConfigurePresetVariables(preset: ConfigurePreset, fo
12861317

12871318
expansionOpts.envOverride = EnvironmentUtils.mergePreserveNull([env, expandedPreset.environment]);
12881319

1320+
expandedPreset.binaryDir = preset.binaryDir;
1321+
12891322
if (preset.__file && preset.__file.version >= 3) {
12901323
// For presets v3+ binaryDir is optional, but cmake-tools needs a value. Default to something reasonable.
12911324
if (!preset.binaryDir) {
12921325
const defaultValue = '${sourceDir}/out/build/${presetName}';
12931326

12941327
log.debug(localize('binaryDir.undefined', 'Configure preset {0}: No binaryDir specified, using default value {1}', preset.name, `"${defaultValue}"`));
1295-
preset.binaryDir = defaultValue;
1328+
// Modify the expandedPreset binary dir so that we don't modify the cache in place.
1329+
expandedPreset.binaryDir = defaultValue;
12961330
}
12971331
}
12981332

12991333
// Expand other fields
1300-
if (preset.binaryDir) {
1301-
expandedPreset.binaryDir = util.lightNormalizePath(await expandString(preset.binaryDir, expansionOpts, errorHandler));
1334+
if (expandedPreset.binaryDir) {
1335+
expandedPreset.binaryDir = util.lightNormalizePath(await expandString(expandedPreset.binaryDir, expansionOpts, errorHandler));
13021336
if (!path.isAbsolute(expandedPreset.binaryDir)) {
13031337
expandedPreset.binaryDir = util.resolvePath(expandedPreset.binaryDir, sourceDir);
13041338
}
@@ -2049,6 +2083,8 @@ export async function expandPackagePresetVariables(preset: PackagePreset, name:
20492083
}
20502084
}
20512085

2086+
expansionOpts.envOverride = EnvironmentUtils.mergePreserveNull([env, expandedPreset.environment]);
2087+
20522088
if (preset.condition) {
20532089
expandedPreset.condition = await expandCondition(preset.condition, expansionOpts, errorHandler);
20542090
}

src/presetsController.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -139,31 +139,37 @@ export class PresetsController implements vscode.Disposable {
139139
}
140140

141141
private readonly _setExpandedPresets = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
142-
preset.setExpandedPresets(folder, presetsFile);
143-
this._presetsChangedEmitter.fire(presetsFile);
142+
const clone = lodash.cloneDeep(presetsFile);
143+
preset.setExpandedPresets(folder, clone);
144+
this._presetsChangedEmitter.fire(clone);
144145
};
145146

146147
private readonly _setExpandedUserPresetsFile = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
147-
preset.setExpandedUserPresetsFile(folder, presetsFile);
148-
this._userPresetsChangedEmitter.fire(presetsFile);
148+
const clone = lodash.cloneDeep(presetsFile);
149+
preset.setExpandedUserPresetsFile(folder, clone);
150+
this._userPresetsChangedEmitter.fire(clone);
149151
};
150152

151153
private readonly _setPresetsPlusIncluded = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
152-
preset.setPresetsPlusIncluded(folder, presetsFile);
153-
this._presetsChangedEmitter.fire(presetsFile);
154+
const clone = lodash.cloneDeep(presetsFile);
155+
preset.setPresetsPlusIncluded(folder, clone);
156+
this._presetsChangedEmitter.fire(clone);
154157
};
155158

156159
private readonly _setUserPresetsPlusIncluded = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
157-
preset.setUserPresetsPlusIncluded(folder, presetsFile);
158-
this._userPresetsChangedEmitter.fire(presetsFile);
160+
const clone = lodash.cloneDeep(presetsFile);
161+
preset.setUserPresetsPlusIncluded(folder, clone);
162+
this._userPresetsChangedEmitter.fire(clone);
159163
};
160164

161165
private readonly _setOriginalPresetsFile = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
162-
preset.setOriginalPresetsFile(folder, presetsFile);
166+
const clone = lodash.cloneDeep(presetsFile);
167+
preset.setOriginalPresetsFile(folder, clone);
163168
};
164169

165170
private readonly _setOriginalUserPresetsFile = (folder: string, presetsFile: preset.PresetsFile | undefined) => {
166-
preset.setOriginalUserPresetsFile(folder, presetsFile);
171+
const clone = lodash.cloneDeep(presetsFile);
172+
preset.setOriginalUserPresetsFile(folder, clone);
167173
};
168174

169175
private async resetPresetsFile(file: string, setExpandedPresets: SetPresetsFileFunc, setPresetsPlusIncluded: SetPresetsFileFunc, setOriginalPresetsFile: SetPresetsFileFunc, fileExistCallback: (fileExists: boolean) => void, referencedFiles: Map<string, preset.PresetsFile | undefined>) {
@@ -176,8 +182,7 @@ export class PresetsController implements vscode.Disposable {
176182
let presetsFile = await this.parsePresetsFile(presetsFileBuffer, file);
177183
referencedFiles.set(file, presetsFile);
178184
if (presetsFile) {
179-
// Parse again so we automatically have a copy by value
180-
setOriginalPresetsFile(this.folderPath, await this.parsePresetsFile(presetsFileBuffer, file));
185+
setOriginalPresetsFile(this.folderPath, presetsFile);
181186
} else {
182187
setOriginalPresetsFile(this.folderPath, undefined);
183188
}
@@ -188,14 +193,12 @@ export class PresetsController implements vscode.Disposable {
188193
this.populatePrivatePresetsFields(presetsFile, file);
189194
await this.mergeIncludeFiles(presetsFile, file, referencedFiles);
190195

191-
const copyOfPresetsFile = lodash.cloneDeep(presetsFile);
192196
// add the include files to the original presets file
193-
setPresetsPlusIncluded(this.folderPath, copyOfPresetsFile);
197+
setPresetsPlusIncluded(this.folderPath, presetsFile);
194198

195-
const copyAgain = lodash.cloneDeep(copyOfPresetsFile);
196199
// set the pre-expanded version so we can call expandPresetsFile on it
197-
setExpandedPresets(this.folderPath, copyAgain);
198-
presetsFile = await this.expandPresetsFile(copyAgain);
200+
setExpandedPresets(this.folderPath, presetsFile);
201+
presetsFile = await this.expandPresetsFile(presetsFile);
199202
}
200203

201204
setExpandedPresets(this.folderPath, presetsFile);
@@ -1718,14 +1721,12 @@ export class PresetsController implements vscode.Disposable {
17181721
return undefined;
17191722
}
17201723

1721-
const clonedPresetsFile = lodash.cloneDeep(presetsFile);
1722-
17231724
log.info(localize('expanding.presets.file', 'Expanding presets file {0}', presetsFile?.__path || ''));
17241725

17251726
const expansionErrors: ExpansionErrorHandler = { errorList: [], tempErrorList: []};
17261727

17271728
const expandedConfigurePresets: preset.ConfigurePreset[] = [];
1728-
for (const configurePreset of clonedPresetsFile?.configurePresets || []) {
1729+
for (const configurePreset of presetsFile?.configurePresets || []) {
17291730
const inheritedPreset = await preset.getConfigurePresetInherits(
17301731
this.folderPath,
17311732
configurePreset.name,
@@ -1747,7 +1748,7 @@ export class PresetsController implements vscode.Disposable {
17471748
}
17481749

17491750
const expandedBuildPresets: preset.BuildPreset[] = [];
1750-
for (const buildPreset of clonedPresetsFile?.buildPresets || []) {
1751+
for (const buildPreset of presetsFile?.buildPresets || []) {
17511752
const inheritedPreset = await preset.getBuildPresetInherits(
17521753
this.folderPath,
17531754
buildPreset.name,
@@ -1771,7 +1772,7 @@ export class PresetsController implements vscode.Disposable {
17711772
}
17721773

17731774
const expandedTestPresets: preset.TestPreset[] = [];
1774-
for (const testPreset of clonedPresetsFile?.testPresets || []) {
1775+
for (const testPreset of presetsFile?.testPresets || []) {
17751776
const inheritedPreset = await preset.getTestPresetInherits(
17761777
this.folderPath,
17771778
testPreset.name,
@@ -1795,7 +1796,7 @@ export class PresetsController implements vscode.Disposable {
17951796
}
17961797

17971798
const expandedPackagePresets: preset.PackagePreset[] = [];
1798-
for (const packagePreset of clonedPresetsFile?.packagePresets || []) {
1799+
for (const packagePreset of presetsFile?.packagePresets || []) {
17991800
const inheritedPreset = await preset.getPackagePresetInherits(
18001801
this.folderPath,
18011802
packagePreset.name,
@@ -1819,7 +1820,7 @@ export class PresetsController implements vscode.Disposable {
18191820
}
18201821

18211822
const expandedWorkflowPresets: preset.WorkflowPreset[] = [];
1822-
for (const workflowPreset of clonedPresetsFile?.workflowPresets || []) {
1823+
for (const workflowPreset of presetsFile?.workflowPresets || []) {
18231824
const inheritedPreset = await preset.getWorkflowPresetInherits(
18241825
this.folderPath,
18251826
workflowPreset.name,
@@ -1837,7 +1838,7 @@ export class PresetsController implements vscode.Disposable {
18371838

18381839
if (expansionErrors.errorList.length > 0) {
18391840
log.error(localize('expansion.errors', 'Expansion errors found in the presets file.'));
1840-
await this.reportPresetsFileErrors(clonedPresetsFile.__path, expansionErrors);
1841+
await this.reportPresetsFileErrors(presetsFile.__path, expansionErrors);
18411842
return undefined;
18421843
} else {
18431844
log.info(localize('successfully.expanded.presets.file', 'Successfully expanded presets file {0}', presetsFile?.__path || ''));
@@ -1851,7 +1852,7 @@ export class PresetsController implements vscode.Disposable {
18511852
presetsFile.workflowPresets = expandedWorkflowPresets;
18521853

18531854
// clear out the errors since there are none now
1854-
collections.presets.set(vscode.Uri.file(clonedPresetsFile.__path || ""), undefined);
1855+
collections.presets.set(vscode.Uri.file(presetsFile.__path || ""), undefined);
18551856

18561857
return presetsFile;
18571858
}

0 commit comments

Comments
 (0)