Skip to content

Commit 0217b9f

Browse files
authored
Allow optional swift.backgroundCompilation object notation for finer control of background compilation (#1857)
* Allow using the default `swift` build task for background compilation This is not related to background indexing, but instead the optional background compilation feature that runs a build task on save. Essentially when `swift.backgroundCompilation.useDefaultTask` is enabled, any `swift` task marked as the default "build" group task will be used instead of the Build All task. This way the whole world is not built for large projects. Issue: #1320 * Add CHANGELOG * Move setting from `backgroundCompilation` subsection - Setting `swift.backgroundCompilation` to `true` was causing the value of `swift.backgroundCompilation.useDefaultTask` to be ignored * Allow `swift.backgroundCompilation` setting to accept an object Object will accept `enabled`, `release`, and `useDefaultTask` properties * Fix tests
1 parent d37b377 commit 0217b9f

File tree

7 files changed

+196
-49
lines changed

7 files changed

+196
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Prompt to restart `SourceKit-LSP` after changing `.sourcekit-lsp/config.json` files ([#1744](https://github.com/swiftlang/vscode-swift/issues/1744))
99
- Prompt to cancel and replace the active test run if one is in flight ([#1774](https://github.com/swiftlang/vscode-swift/pull/1774))
1010
- A walkthrough for first time extension users ([#1560](https://github.com/swiftlang/vscode-swift/issues/1560))
11+
- Allow `swift.backgroundCompilation` setting to accept an object where enabling the `useDefaultTask` property will run the default build task, and the `release` property will run the `release` variant of the Build All task ([#1857](https://github.com/swiftlang/vscode-swift/pull/1857))
1112

1213
### Fixed
1314

package.json

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,10 +593,29 @@
593593
"scope": "machine-overridable"
594594
},
595595
"swift.backgroundCompilation": {
596-
"type": "boolean",
596+
"type": [
597+
"boolean",
598+
"object"
599+
],
597600
"default": false,
598-
"markdownDescription": "**Experimental**: Run `swift build` in the background whenever a file is saved. It is possible the background compilation will already be running when you attempt a compile yourself, so this is disabled by default.",
599-
"scope": "machine-overridable"
601+
"markdownDescription": "Run `swift build` in the background whenever a file is saved. Setting to `true` enables, or you can use `object` notation for more fine grained control. It is possible the background compilation will already be running when you attempt a compile yourself, so this is disabled by default.",
602+
"properties": {
603+
"enabled": {
604+
"type": "boolean",
605+
"default": true,
606+
"description": "Enable/disable background compilation."
607+
},
608+
"useDefaultTask": {
609+
"type": "boolean",
610+
"default": true,
611+
"markdownDescription": "Use the default build task configured using the `Tasks: Configure Default Build Task` command when executing the background compilation. `#enabled#` property must be `true`."
612+
},
613+
"release": {
614+
"type": "boolean",
615+
"default": false,
616+
"markdownDescription": "Use the `release` variant of the `Build All` task when executing the background compilation. `#enabled#` property must be `true`. This is ignored if the `#useDefaultTask#` property is true."
617+
}
618+
}
600619
},
601620
"swift.actionAfterBuildError": {
602621
"type": "string",

src/BackgroundCompilation.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ export class BackgroundCompilation implements vscode.Disposable {
3131
// We only want to configure the file watcher if background compilation is enabled.
3232
this.configurationEventDisposable = vscode.workspace.onDidChangeConfiguration(event => {
3333
if (event.affectsConfiguration("swift.backgroundCompilation", folderContext.folder)) {
34-
if (configuration.backgroundCompilation) {
34+
if (configuration.backgroundCompilation.enabled) {
3535
this.setupFileWatching();
3636
} else {
3737
this.stopFileWatching();
3838
}
3939
}
4040
});
4141

42-
if (configuration.backgroundCompilation) {
42+
if (configuration.backgroundCompilation.enabled) {
4343
this.setupFileWatching();
4444
}
4545
}
@@ -86,7 +86,7 @@ export class BackgroundCompilation implements vscode.Disposable {
8686
*/
8787
async runTask() {
8888
// create compile task and execute it
89-
const backgroundTask = await getBuildAllTask(this.folderContext);
89+
const backgroundTask = await this.getTask();
9090
if (!backgroundTask) {
9191
return;
9292
}
@@ -96,4 +96,12 @@ export class BackgroundCompilation implements vscode.Disposable {
9696
// can ignore if running task fails
9797
}
9898
}
99+
100+
async getTask(): Promise<vscode.Task> {
101+
return await getBuildAllTask(
102+
this.folderContext,
103+
configuration.backgroundCompilation.release,
104+
configuration.backgroundCompilation.useDefaultTask
105+
);
106+
}
99107
}

src/configuration.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ export interface PluginPermissionConfiguration {
105105
allowNetworkConnections?: string;
106106
}
107107

108+
export interface BackgroundCompilationConfiguration {
109+
/** enable background compilation task on save */
110+
enabled: boolean;
111+
/** use the default `swift` build task when background compilation is enabled */
112+
useDefaultTask: boolean;
113+
/** Use the `release` variant of the build all task */
114+
release: boolean;
115+
}
116+
108117
/**
109118
* Type-safe wrapper around configuration settings.
110119
*/
@@ -414,10 +423,21 @@ const configuration = {
414423
.get<boolean>("createTasksForLibraryProducts", false);
415424
},
416425
/** background compilation */
417-
get backgroundCompilation(): boolean {
418-
return vscode.workspace
426+
get backgroundCompilation(): BackgroundCompilationConfiguration {
427+
const value = vscode.workspace
419428
.getConfiguration("swift")
420-
.get<boolean>("backgroundCompilation", false);
429+
.get<BackgroundCompilationConfiguration | boolean>("backgroundCompilation", false);
430+
return {
431+
get enabled(): boolean {
432+
return typeof value === "boolean" ? value : value.enabled;
433+
},
434+
get useDefaultTask(): boolean {
435+
return typeof value === "boolean" ? true : (value.useDefaultTask ?? true);
436+
},
437+
get release(): boolean {
438+
return typeof value === "boolean" ? false : (value.release ?? false);
439+
},
440+
};
421441
},
422442
/** background indexing */
423443
get backgroundIndexing(): "on" | "off" | "auto" {

src/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,13 @@ function handleFolderEvent(logger: SwiftLogger): (event: FolderEvent) => Promise
189189
async function folderAdded(folder: FolderContext, workspace: WorkspaceContext) {
190190
if (
191191
!configuration.folder(folder.workspaceFolder).disableAutoResolve ||
192-
configuration.backgroundCompilation
192+
configuration.backgroundCompilation.enabled
193193
) {
194194
// if background compilation is set then run compile at startup unless
195195
// this folder is a sub-folder of the workspace folder. This is to avoid
196196
// kicking off compile for multiple projects at the same time
197197
if (
198-
configuration.backgroundCompilation &&
198+
configuration.backgroundCompilation.enabled &&
199199
folder.workspaceFolder.uri === folder.folder
200200
) {
201201
await folder.backgroundCompilation.runTask();

src/tasks/SwiftTaskProvider.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ export async function createBuildAllTask(
183183
*/
184184
export async function getBuildAllTask(
185185
folderContext: FolderContext,
186-
release: boolean = false
186+
release: boolean = false,
187+
findDefault: boolean = true
187188
): Promise<vscode.Task> {
188189
const buildTaskName = buildAllTaskName(folderContext, release);
189190
const folderWorkingDir = folderContext.workspaceFolder.uri.fsPath;
@@ -208,11 +209,14 @@ export async function getBuildAllTask(
208209
});
209210

210211
// find default build task
211-
let task = workspaceTasks.find(
212-
task => task.group?.id === vscode.TaskGroup.Build.id && task.group?.isDefault === true
213-
);
214-
if (task) {
215-
return task;
212+
let task;
213+
if (findDefault) {
214+
task = workspaceTasks.find(
215+
task => task.group?.id === vscode.TaskGroup.Build.id && task.group?.isDefault === true
216+
);
217+
if (task) {
218+
return task;
219+
}
216220
}
217221
// find task with name "swift: Build All"
218222
task = workspaceTasks.find(task => task.name === `swift: ${buildTaskName}`);

test/integration-tests/BackgroundCompilation.test.ts

Lines changed: 127 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,55 +11,150 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14-
import * as assert from "assert";
14+
import { expect } from "chai";
1515
import * as vscode from "vscode";
1616

17+
import { BackgroundCompilation } from "@src/BackgroundCompilation";
18+
import { FolderContext } from "@src/FolderContext";
1719
import { WorkspaceContext } from "@src/WorkspaceContext";
20+
import { createSwiftTask, getBuildAllTask } from "@src/tasks/SwiftTaskProvider";
1821

22+
import { mockGlobalObject } from "../MockUtils";
1923
import { testAssetUri } from "../fixtures";
2024
import { tag } from "../tags";
2125
import { closeAllEditors } from "../utilities/commands";
2226
import { waitForNoRunningTasks } from "../utilities/tasks";
23-
import { activateExtensionForTest, updateSettings } from "./utilities/testutilities";
27+
import {
28+
activateExtensionForSuite,
29+
folderInRootWorkspace,
30+
updateSettings,
31+
} from "./utilities/testutilities";
2432

2533
tag("large").suite("BackgroundCompilation Test Suite", () => {
26-
let subscriptions: vscode.Disposable[];
2734
let workspaceContext: WorkspaceContext;
35+
let folderContext: FolderContext;
36+
let buildAllTask: vscode.Task;
2837

29-
activateExtensionForTest({
30-
async setup(ctx) {
31-
subscriptions = [];
32-
workspaceContext = ctx;
33-
assert.notEqual(workspaceContext.folders.length, 0);
34-
return await updateSettings({
35-
"swift.backgroundCompilation": true,
38+
async function setupFolder(ctx: WorkspaceContext) {
39+
workspaceContext = ctx;
40+
folderContext = await folderInRootWorkspace("defaultPackage", workspaceContext);
41+
buildAllTask = await getBuildAllTask(folderContext);
42+
}
43+
44+
suite("build all on save", () => {
45+
let subscriptions: vscode.Disposable[];
46+
47+
activateExtensionForSuite({
48+
async setup(ctx) {
49+
subscriptions = [];
50+
await setupFolder(ctx);
51+
return await updateSettings({
52+
"swift.backgroundCompilation": true,
53+
});
54+
},
55+
});
56+
57+
suiteTeardown(async () => {
58+
subscriptions.forEach(s => s.dispose());
59+
await closeAllEditors();
60+
});
61+
62+
test("runs build task", async () => {
63+
const taskStartPromise = new Promise<void>(resolve => {
64+
subscriptions.push(
65+
vscode.tasks.onDidStartTask(e => {
66+
const task = e.execution.task;
67+
if (task.name.includes("Build All")) {
68+
resolve();
69+
}
70+
})
71+
);
3672
});
37-
},
38-
});
3973

40-
suiteTeardown(async () => {
41-
subscriptions.forEach(s => s.dispose());
42-
await closeAllEditors();
43-
});
74+
const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift");
75+
const doc = await vscode.workspace.openTextDocument(uri.fsPath);
76+
await vscode.window.showTextDocument(doc);
77+
await vscode.workspace.save(uri);
4478

45-
test("build all on save", async () => {
46-
const taskStartPromise = new Promise<void>(resolve => {
47-
subscriptions.push(
48-
vscode.tasks.onDidStartTask(e => {
49-
const task = e.execution.task;
50-
if (task.name.includes("Build All")) {
51-
resolve();
52-
}
53-
})
54-
);
79+
await taskStartPromise;
80+
await waitForNoRunningTasks();
5581
});
82+
});
5683

57-
const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift");
58-
const doc = await vscode.workspace.openTextDocument(uri.fsPath);
59-
await vscode.window.showTextDocument(doc);
60-
await vscode.workspace.save(uri);
84+
suite("getTask", () => {
85+
const tasksMock = mockGlobalObject(vscode, "tasks");
86+
let swiftTask: vscode.Task;
87+
let nonSwiftTask: vscode.Task;
88+
let backgroundConfiguration: BackgroundCompilation;
89+
90+
suite("useDefaultTask", () => {
91+
activateExtensionForSuite({
92+
async setup(ctx) {
93+
await setupFolder(ctx);
94+
nonSwiftTask = new vscode.Task(
95+
{
96+
type: "shell",
97+
command: ["swift"],
98+
args: ["build"],
99+
group: {
100+
id: "build",
101+
isDefault: true,
102+
},
103+
label: "shell build",
104+
},
105+
folderContext.workspaceFolder,
106+
"shell build",
107+
"Workspace",
108+
new vscode.ShellExecution("", {
109+
cwd: testAssetUri("defaultPackage").fsPath,
110+
})
111+
);
112+
swiftTask = createSwiftTask(
113+
["build"],
114+
"swift build",
115+
{
116+
cwd: testAssetUri("defaultPackage"),
117+
scope: folderContext.workspaceFolder,
118+
},
119+
folderContext.toolchain
120+
);
121+
swiftTask.source = "Workspace";
122+
return await updateSettings({
123+
"swift.backgroundCompilation": {
124+
enabled: true,
125+
useDefaultTask: true,
126+
},
127+
});
128+
},
129+
});
61130

62-
await taskStartPromise;
63-
await waitForNoRunningTasks();
131+
setup(() => {
132+
tasksMock.fetchTasks.withArgs().resolves([nonSwiftTask, swiftTask, buildAllTask]);
133+
backgroundConfiguration = new BackgroundCompilation(folderContext);
134+
});
135+
136+
teardown(() => {
137+
backgroundConfiguration.dispose();
138+
});
139+
140+
test("swift default task", async () => {
141+
swiftTask.group = { id: "build", isDefault: true };
142+
expect(await backgroundConfiguration.getTask()).to.equal(swiftTask);
143+
});
144+
145+
test("non-swift default task", async () => {
146+
nonSwiftTask.group = { id: "build", isDefault: true };
147+
expect(await backgroundConfiguration.getTask()).to.equal(nonSwiftTask);
148+
});
149+
150+
test("don't use default task", async () => {
151+
swiftTask.group = { id: "build", isDefault: true };
152+
await vscode.workspace.getConfiguration("swift").update("backgroundCompilation", {
153+
enabled: true,
154+
useDefaultTask: false,
155+
});
156+
expect(await backgroundConfiguration.getTask()).to.equal(buildAllTask);
157+
});
158+
});
64159
});
65160
});

0 commit comments

Comments
 (0)