Skip to content

Commit c779bf1

Browse files
Ensure we correctly inherit preset from UserPreset (#3958)
* fix * analyze user presets first because it's the base if it exists * more updates to fix include files and telemetry, still needs work and testing * ensure telemetry and other uses of 'all..Presets' is right * more fixes * ensure we don't duplicate cmakepresets in userpresets includes * remove unnecessary return * unionWith consistency * add test
1 parent ec03fc1 commit c779bf1

File tree

5 files changed

+90
-28
lines changed

5 files changed

+90
-28
lines changed

src/drivers/cmakeDriver.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import * as path from 'path';
66
import * as vscode from 'vscode';
7+
import * as lodash from "lodash";
78

89
import { CMakeExecutable } from '@cmt/cmake/cmakeExecutable';
910
import * as codepages from '@cmt/codePageTable';
@@ -1568,15 +1569,15 @@ export abstract class CMakeDriver implements vscode.Disposable {
15681569
};
15691570
if (this.useCMakePresets && this.workspaceFolder) {
15701571
const configurePresets = preset.configurePresets(this.workspaceFolder);
1571-
const userConfigurePresets = preset.userConfigurePresets(this.workspaceFolder);
1572+
const userConfigurePresets = lodash.differenceWith(preset.userConfigurePresets(this.workspaceFolder), configurePresets, (a, b) => a.name === b.name);
15721573
const buildPresets = preset.buildPresets(this.workspaceFolder);
1573-
const userBuildPresets = preset.userBuildPresets(this.workspaceFolder);
1574+
const userBuildPresets = lodash.differenceWith(preset.userBuildPresets(this.workspaceFolder), buildPresets, (a, b) => a.name === b.name);
15741575
const testPresets = preset.testPresets(this.workspaceFolder);
1575-
const userTestPresets = preset.userTestPresets(this.workspaceFolder);
1576+
const userTestPresets = lodash.differenceWith(preset.userTestPresets(this.workspaceFolder), testPresets, (a, b) => a.name === b.name);
15761577
const packagePresets = preset.packagePresets(this.workspaceFolder);
1577-
const userPackagePresets = preset.userPackagePresets(this.workspaceFolder);
1578+
const userPackagePresets = lodash.differenceWith(preset.userPackagePresets(this.workspaceFolder), packagePresets, (a, b) => a.name === b.name);
15781579
const workflowPresets = preset.workflowPresets(this.workspaceFolder);
1579-
const userWorkflowPresets = preset.userWorkflowPresets(this.workspaceFolder);
1580+
const userWorkflowPresets = lodash.differenceWith(preset.userWorkflowPresets(this.workspaceFolder), workflowPresets, (a, b) => a.name === b.name);
15801581
telemetryMeasures['ConfigurePresets'] = configurePresets.length;
15811582
telemetryMeasures['HiddenConfigurePresets'] = this.countHiddenPresets(configurePresets);
15821583
telemetryMeasures['UserConfigurePresets'] = userConfigurePresets.length;

src/preset.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import * as nls from 'vscode-nls';
33
import * as path from 'path';
44
import * as vscode from "vscode";
5+
import * as lodash from "lodash";
56

67
import * as util from '@cmt/util';
78
import * as logging from '@cmt/logging';
@@ -540,7 +541,7 @@ export function userConfigurePresets(folder: string, usePresetsPlusIncluded: boo
540541
* Don't use this function if you need to keep any changes in the presets
541542
*/
542543
export function allConfigurePresets(folder: string, usePresetsPlusIncluded: boolean = false) {
543-
return configurePresets(folder, usePresetsPlusIncluded).concat(userConfigurePresets(folder, usePresetsPlusIncluded));
544+
return lodash.unionWith(configurePresets(folder, usePresetsPlusIncluded).concat(userConfigurePresets(folder, usePresetsPlusIncluded)), (a, b) => a.name === b.name);
544545
}
545546

546547
export function buildPresets(folder: string) {
@@ -555,7 +556,7 @@ export function userBuildPresets(folder: string) {
555556
* Don't use this function if you need to keep any changes in the presets
556557
*/
557558
export function allBuildPresets(folder: string) {
558-
return buildPresets(folder).concat(userBuildPresets(folder));
559+
return lodash.unionWith(buildPresets(folder).concat(userBuildPresets(folder)), (a, b) => a.name === b.name);
559560
}
560561

561562
export function testPresets(folder: string) {
@@ -570,7 +571,7 @@ export function userTestPresets(folder: string) {
570571
* Don't use this function if you need to keep any changes in the presets
571572
*/
572573
export function allTestPresets(folder: string) {
573-
return testPresets(folder).concat(userTestPresets(folder));
574+
return lodash.unionWith(testPresets(folder).concat(userTestPresets(folder)), (a, b) => a.name === b.name);
574575
}
575576

576577
export function packagePresets(folder: string) {
@@ -585,7 +586,7 @@ export function userPackagePresets(folder: string) {
585586
* Don't use this function if you need to keep any changes in the presets
586587
*/
587588
export function allPackagePresets(folder: string) {
588-
return packagePresets(folder).concat(userPackagePresets(folder));
589+
return lodash.unionWith(packagePresets(folder).concat(userPackagePresets(folder)), (a, b) => a.name === b.name);
589590
}
590591

591592
export function workflowPresets(folder: string) {
@@ -600,7 +601,7 @@ export function userWorkflowPresets(folder: string) {
600601
* Don't use this function if you need to keep any changes in the presets
601602
*/
602603
export function allWorkflowPresets(folder: string) {
603-
return workflowPresets(folder).concat(userWorkflowPresets(folder));
604+
return lodash.unionWith(workflowPresets(folder).concat(userWorkflowPresets(folder)), (a, b) => a.name === b.name);
604605
}
605606

606607
export function getPresetByName<T extends Preset>(presets: T[], name: string): T | null {

src/presetsController.ts

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,15 @@ export class PresetsController implements vscode.Disposable {
166166
preset.setOriginalUserPresetsFile(folder, presetsFile);
167167
};
168168

169-
private async resetPresetsFile(file: string, setExpandedPresets: SetPresetsFileFunc, setPresetsPlusIncluded: SetPresetsFileFunc, setOriginalPresetsFile: SetPresetsFileFunc, fileExistCallback: (fileExists: boolean) => void, referencedFiles: Set<string>) {
169+
private async resetPresetsFile(file: string, setExpandedPresets: SetPresetsFileFunc, setPresetsPlusIncluded: SetPresetsFileFunc, setOriginalPresetsFile: SetPresetsFileFunc, fileExistCallback: (fileExists: boolean) => void, referencedFiles: Map<string, preset.PresetsFile | undefined>) {
170170
const presetsFileBuffer = await this.readPresetsFile(file);
171171

172172
// There might be a better location for this, but for now this is the best one...
173173
fileExistCallback(Boolean(presetsFileBuffer));
174174

175175
// Record the file as referenced, even if the file does not exist.
176-
referencedFiles.add(file);
177176
let presetsFile = await this.parsePresetsFile(presetsFileBuffer, file);
177+
referencedFiles.set(file, presetsFile);
178178
if (presetsFile) {
179179
// Parse again so we automatically have a copy by value
180180
setOriginalPresetsFile(this.folderPath, await this.parsePresetsFile(presetsFileBuffer, file));
@@ -203,14 +203,14 @@ export class PresetsController implements vscode.Disposable {
203203
// Need to reapply presets every time presets changed since the binary dir or cmake path could change
204204
// (need to clean or reload driver)
205205
async reapplyPresets() {
206-
const referencedFiles: Set<string> = new Set();
206+
const referencedFiles: Map<string, preset.PresetsFile | undefined> = new Map();
207207

208208
// Reset all changes due to expansion since parents could change
209209
await this.resetPresetsFile(this.presetsPath, this._setExpandedPresets, this._setPresetsPlusIncluded, this._setOriginalPresetsFile, exists => this._presetsFileExists = exists, referencedFiles);
210210
await this.resetPresetsFile(this.userPresetsPath, this._setExpandedUserPresetsFile, this._setUserPresetsPlusIncluded, this._setOriginalUserPresetsFile, exists => this._userPresetsFileExists = exists, referencedFiles);
211211

212212
// reset all expanded presets storage.
213-
this._referencedFiles = Array.from(referencedFiles);
213+
this._referencedFiles = Array.from(referencedFiles.keys());
214214

215215
this.project.minCMakeVersion = preset.minCMakeVersion(this.folderPath);
216216

@@ -837,24 +837,31 @@ export class PresetsController implements vscode.Disposable {
837837
return chosenPresets?.preset;
838838
}
839839

840+
// For all of the `getAll` methods, we now can safely grab only the user presets (if present), because they inherently include
841+
// the presets.
840842
async getAllConfigurePresets(): Promise<preset.ConfigurePreset[]> {
841-
return preset.configurePresets(this.folderPath).concat(preset.userConfigurePresets(this.folderPath));
843+
const userPresets = preset.userConfigurePresets(this.folderPath);
844+
return userPresets.length > 0 ? userPresets : preset.configurePresets(this.folderPath);
842845
}
843846

844847
async getAllBuildPresets(): Promise<preset.BuildPreset[]> {
845-
return preset.buildPresets(this.folderPath).concat(preset.userBuildPresets(this.folderPath));
848+
const userPresets = preset.userBuildPresets(this.folderPath);
849+
return userPresets.length > 0 ? userPresets : preset.buildPresets(this.folderPath);
846850
}
847851

848852
async getAllTestPresets(): Promise<preset.TestPreset[]> {
849-
return preset.testPresets(this.folderPath).concat(preset.userTestPresets(this.folderPath));
853+
const userPresets = preset.userTestPresets(this.folderPath);
854+
return userPresets.length > 0 ? userPresets : preset.testPresets(this.folderPath);
850855
}
851856

852857
async getAllPackagePresets(): Promise<preset.PackagePreset[]> {
853-
return preset.packagePresets(this.folderPath).concat(preset.userPackagePresets(this.folderPath));
858+
const userPresets = preset.userPackagePresets(this.folderPath);
859+
return userPresets.length > 0 ? userPresets : preset.packagePresets(this.folderPath);
854860
}
855861

856862
async getAllWorkflowPresets(): Promise<preset.WorkflowPreset[]> {
857-
return preset.workflowPresets(this.folderPath).concat(preset.userWorkflowPresets(this.folderPath));
863+
const userPresets = preset.userWorkflowPresets(this.folderPath);
864+
return userPresets.length > 0 ? userPresets : preset.workflowPresets(this.folderPath);
858865
}
859866

860867
async selectConfigurePreset(quickStart?: boolean): Promise<boolean> {
@@ -1594,8 +1601,28 @@ export class PresetsController implements vscode.Disposable {
15941601
setFile(presetsFile.packagePresets);
15951602
}
15961603

1597-
private async mergeIncludeFiles(presetsFile: preset.PresetsFile | undefined, file: string, referencedFiles: Set<string>): Promise<void> {
1598-
if (!presetsFile || !presetsFile.include) {
1604+
private async mergeIncludeFiles(presetsFile: preset.PresetsFile | undefined, file: string, referencedFiles: Map<string, preset.PresetsFile | undefined>): Promise<void> {
1605+
if (!presetsFile) {
1606+
return;
1607+
}
1608+
1609+
// CMakeUserPresets.json file should include CMakePresets.json file, by default.
1610+
if (this.presetsFileExist && file === this.userPresetsPath) {
1611+
presetsFile.include = presetsFile.include || [];
1612+
const filteredIncludes = presetsFile.include.filter(include => {
1613+
// Ensuring that we handle expansions. Duplicated from loop below.
1614+
const includePath = presetsFile.version >= 7 ?
1615+
// Version 7 and later support $penv{} expansions in include paths
1616+
substituteAll(include, getParentEnvSubstitutions(include, new Map<string, string>())).result :
1617+
include;
1618+
path.normalize(path.resolve(path.dirname(file), includePath)) === this.presetsPath;
1619+
});
1620+
if (filteredIncludes.length === 0) {
1621+
presetsFile.include.push(this.presetsPath);
1622+
}
1623+
}
1624+
1625+
if (!presetsFile.include) {
15991626
return;
16001627
}
16011628

@@ -1610,10 +1637,33 @@ export class PresetsController implements vscode.Disposable {
16101637

16111638
// Do not include files more than once
16121639
if (referencedFiles.has(fullIncludePath)) {
1640+
const referencedIncludeFile = referencedFiles.get(fullIncludePath);
1641+
if (referencedIncludeFile) {
1642+
if (referencedIncludeFile.configurePresets) {
1643+
presetsFile.configurePresets = lodash.unionWith(referencedIncludeFile.configurePresets, presetsFile.configurePresets || [], (a, b) => a.name === b.name);
1644+
}
1645+
if (referencedIncludeFile.buildPresets) {
1646+
presetsFile.buildPresets = lodash.unionWith(referencedIncludeFile.buildPresets, presetsFile.buildPresets || [], (a, b) => a.name === b.name);
1647+
}
1648+
if (referencedIncludeFile.testPresets) {
1649+
presetsFile.testPresets = lodash.unionWith(referencedIncludeFile.testPresets, presetsFile.testPresets || [], (a, b) => a.name === b.name);
1650+
}
1651+
if (referencedIncludeFile.packagePresets) {
1652+
presetsFile.packagePresets = lodash.unionWith(referencedIncludeFile.packagePresets, presetsFile.packagePresets || [], (a, b) => a.name === b.name);
1653+
}
1654+
if (referencedIncludeFile.workflowPresets) {
1655+
presetsFile.workflowPresets = lodash.unionWith(referencedIncludeFile.workflowPresets, presetsFile.workflowPresets || [], (a, b) => a.name === b.name);
1656+
}
1657+
if (referencedIncludeFile.cmakeMinimumRequired) {
1658+
if (!presetsFile.cmakeMinimumRequired || util.versionLess(presetsFile.cmakeMinimumRequired, referencedIncludeFile.cmakeMinimumRequired)) {
1659+
presetsFile.cmakeMinimumRequired = referencedIncludeFile.cmakeMinimumRequired;
1660+
}
1661+
}
1662+
}
16131663
continue;
16141664
}
16151665
// Record the file as referenced, even if the file does not exist.
1616-
referencedFiles.add(fullIncludePath);
1666+
referencedFiles.set(fullIncludePath, undefined);
16171667

16181668
const includeFileBuffer = await this.readPresetsFile(fullIncludePath);
16191669
if (!includeFileBuffer) {
@@ -1622,6 +1672,7 @@ export class PresetsController implements vscode.Disposable {
16221672
}
16231673

16241674
let includeFile = await this.parsePresetsFile(includeFileBuffer, fullIncludePath);
1675+
referencedFiles.set(fullIncludePath, includeFile);
16251676
includeFile = await this.validatePresetsFile(includeFile, fullIncludePath);
16261677
if (!includeFile) {
16271678
continue;
@@ -1634,19 +1685,19 @@ export class PresetsController implements vscode.Disposable {
16341685
await this.mergeIncludeFiles(includeFile, fullIncludePath, referencedFiles);
16351686

16361687
if (includeFile.configurePresets) {
1637-
presetsFile.configurePresets = includeFile.configurePresets.concat(presetsFile.configurePresets || []);
1688+
presetsFile.configurePresets = lodash.unionWith(includeFile.configurePresets, presetsFile.configurePresets || [], (a, b) => a.name === b.name);
16381689
}
16391690
if (includeFile.buildPresets) {
1640-
presetsFile.buildPresets = includeFile.buildPresets.concat(presetsFile.buildPresets || []);
1691+
presetsFile.buildPresets = lodash.unionWith(includeFile.buildPresets, presetsFile.buildPresets || [], (a, b) => a.name === b.name);
16411692
}
16421693
if (includeFile.testPresets) {
1643-
presetsFile.testPresets = includeFile.testPresets.concat(presetsFile.testPresets || []);
1694+
presetsFile.testPresets = lodash.unionWith(includeFile.testPresets, presetsFile.testPresets || [], (a, b) => a.name === b.name);
16441695
}
16451696
if (includeFile.packagePresets) {
1646-
presetsFile.packagePresets = includeFile.packagePresets.concat(presetsFile.packagePresets || []);
1697+
presetsFile.packagePresets = lodash.unionWith(includeFile.packagePresets, presetsFile.packagePresets || [], (a, b) => a.name === b.name);
16471698
}
16481699
if (includeFile.workflowPresets) {
1649-
presetsFile.workflowPresets = includeFile.workflowPresets.concat(presetsFile.workflowPresets || []);
1700+
presetsFile.workflowPresets = lodash.unionWith(includeFile.workflowPresets, presetsFile.workflowPresets || [], (a, b) => a.name === b.name);
16501701
}
16511702
if (includeFile.cmakeMinimumRequired) {
16521703
if (!presetsFile.cmakeMinimumRequired || util.versionLess(presetsFile.cmakeMinimumRequired, includeFile.cmakeMinimumRequired)) {

test/extension-tests/single-root-UI/project-folder/CMakeUserPresets.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
"environment": {
4545
"TEST_VARIANT_ENV": "0cbfb6ae-f2ec-4017-8ded-89df8759c502"
4646
}
47+
},
48+
{
49+
"name": "TestInheritFromPreset",
50+
"inherits": "Linux1"
4751
}
4852
]
49-
}
53+
}

test/extension-tests/single-root-UI/test/include-presets.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,9 @@ suite('Preset include functionality', () => {
6969
const result = await testEnv.result.getResultAsJson();
7070
expect(result['cookie']).to.eq('passed-cookie');
7171
}).timeout(100000);
72+
73+
test('Configure CMakeUserPreset inheriting from CMakePreset', async function (this: Mocha.Context) {
74+
await vscode.commands.executeCommand('cmake.setConfigurePreset', 'TestInheritFromPreset');
75+
expect(await vscode.commands.executeCommand('cmake.showConfigureCommand')).to.be.eq(0);
76+
}).timeout(100000);
7277
});

0 commit comments

Comments
 (0)