Skip to content

Commit 4121e2e

Browse files
authored
Set swift.swiftSDK for target platforms (#1390)
* Set swift.swiftSDK for target platforms SourceKit-LSP now provides code editing support for non-macOS Darwin platforms starting Swift 6.1 using the --swift-sdk flag. Set this flag to the appropriate target triple when using the "Select Target Platform" feature on macOS. Issue: #1335 * Restrict Switch Platform to macOS and Swift 6.1 Currently this command only supports Darwin SDKs on macOS, and code editing support from Swift 6.1 and above. Only show the command for these cases. Also add a unit test for the command. Issue: #1335
1 parent e659d4c commit 4121e2e

File tree

7 files changed

+140
-27
lines changed

7 files changed

+140
-27
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ The extension adds the following commands, available via the command palette.
4646

4747
The following command is only available on macOS:
4848

49-
- **Select Target Platform**: This is an experimental command that offers code completion for iOS and tvOS projects.
49+
- **Select Target Platform**: This is an experimental command that offers code editing support for iOS, tvOS, watchOS and visionOS projects.
5050

5151
#### Building and Debugging
5252

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@
884884
},
885885
{
886886
"command": "swift.switchPlatform",
887-
"when": "swift.isActivated && isMac"
887+
"when": "swift.isActivated && isMac && swift.switchPlatformAvailable"
888888
},
889889
{
890890
"command": "swift.insertFunctionComment",

src/commands.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,10 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
124124
return runTestMultipleTimes(ctx.currentFolder, item, true);
125125
}
126126
}),
127-
// Note: This is only available on macOS (gated in `package.json`) because its the only OS that has the iOS SDK available.
128-
vscode.commands.registerCommand("swift.switchPlatform", () => switchPlatform()),
127+
// Note: switchPlatform is only available on macOS and Swift 6.1 or later
128+
// (gated in `package.json`) because it's the only OS and toolchain combination that
129+
// has Darwin SDKs available and supports code editing with SourceKit-LSP
130+
vscode.commands.registerCommand("swift.switchPlatform", () => switchPlatform(ctx)),
129131
vscode.commands.registerCommand(Commands.RESET_PACKAGE, () => resetPackage(ctx)),
130132
vscode.commands.registerCommand("swift.runScript", () => runSwiftScript(ctx)),
131133
vscode.commands.registerCommand("swift.openPackage", () => {

src/commands/switchPlatform.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import * as vscode from "vscode";
16-
import { DarwinCompatibleTarget, SwiftToolchain } from "../toolchain/toolchain";
16+
import {
17+
DarwinCompatibleTarget,
18+
SwiftToolchain,
19+
getDarwinTargetTriple,
20+
} from "../toolchain/toolchain";
1721
import configuration from "../configuration";
22+
import { WorkspaceContext } from "../WorkspaceContext";
1823

1924
/**
20-
* Switches the target SDK to the platform selected in a QuickPick UI.
25+
* Switches the appropriate SDK setting to the platform selected in a QuickPick UI.
2126
*/
22-
export async function switchPlatform() {
27+
export async function switchPlatform(ctx: WorkspaceContext) {
2328
const picked = await vscode.window.showQuickPick(
2429
[
2530
{ value: undefined, label: "macOS" },
@@ -29,28 +34,34 @@ export async function switchPlatform() {
2934
{ value: DarwinCompatibleTarget.visionOS, label: "visionOS" },
3035
],
3136
{
32-
placeHolder: "Select a new target",
37+
placeHolder: "Select a new target platform",
3338
}
3439
);
3540
if (picked) {
41+
// show a status item as getSDKForTarget can sometimes take a long while to run xcrun to find the SDK
42+
const statusItemText = `Setting target platform to ${picked.label}`;
43+
ctx.statusItem.start(statusItemText);
3644
try {
37-
const sdkForTarget = picked.value
38-
? await SwiftToolchain.getSDKForTarget(picked.value)
39-
: "";
40-
if (sdkForTarget !== undefined) {
41-
if (sdkForTarget !== "") {
42-
configuration.sdk = sdkForTarget;
43-
vscode.window.showWarningMessage(
44-
`Selecting the ${picked.label} SDK will provide code editing support, but compiling with this SDK will have undefined results.`
45-
);
46-
} else {
47-
configuration.sdk = undefined;
48-
}
45+
if (picked.value) {
46+
// verify that the SDK for the platform actually exists
47+
await SwiftToolchain.getSDKForTarget(picked.value);
48+
}
49+
const swiftSDKTriple = picked.value ? getDarwinTargetTriple(picked.value) : "";
50+
if (swiftSDKTriple !== "") {
51+
// set a swiftSDK for non-macOS Darwin platforms so that SourceKit-LSP can provide syntax highlighting
52+
configuration.swiftSDK = swiftSDKTriple;
53+
vscode.window.showWarningMessage(
54+
`Selecting the ${picked.label} target platform will provide code editing support, but compiling with a ${picked.label} SDK will have undefined results.`
55+
);
4956
} else {
50-
vscode.window.showErrorMessage("Unable to obtain requested SDK path");
57+
// set swiftSDK to an empty string for macOS and other platforms
58+
configuration.swiftSDK = "";
5159
}
5260
} catch {
53-
vscode.window.showErrorMessage("Unable to obtain requested SDK path");
61+
vscode.window.showErrorMessage(
62+
`Unable set the Swift SDK setting to ${picked.label}, verify that the SDK exists`
63+
);
5464
}
65+
ctx.statusItem.end(statusItemText);
5566
}
5667
}

src/contextKeys.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ interface ContextKeys {
7777
* Whether the SourceKit-LSP server supports documentation live preview.
7878
*/
7979
supportsDocumentationLivePreview: boolean;
80+
81+
/**
82+
* Whether the swift.switchPlatform command is available.
83+
*/
84+
switchPlatformAvailable: boolean;
8085
}
8186

8287
/** Creates the getters and setters for the VS Code Swift extension's context keys. */
@@ -92,6 +97,7 @@ function createContextKeys(): ContextKeys {
9297
let createNewProjectAvailable: boolean = false;
9398
let supportsReindexing: boolean = false;
9499
let supportsDocumentationLivePreview: boolean = false;
100+
let switchPlatformAvailable: boolean = false;
95101

96102
return {
97103
get isActivated() {
@@ -200,6 +206,15 @@ function createContextKeys(): ContextKeys {
200206
value
201207
);
202208
},
209+
210+
get switchPlatformAvailable() {
211+
return switchPlatformAvailable;
212+
},
213+
214+
set switchPlatformAvailable(value: boolean) {
215+
switchPlatformAvailable = value;
216+
vscode.commands.executeCommand("setContext", "swift.switchPlatformAvailable", value);
217+
},
203218
};
204219
}
205220

src/extension.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api> {
7676
contextKeys.createNewProjectAvailable = toolchain.swiftVersion.isGreaterThanOrEqual(
7777
new Version(5, 8, 0)
7878
);
79+
contextKeys.switchPlatformAvailable = toolchain.swiftVersion.isGreaterThanOrEqual(
80+
new Version(6, 1, 0)
81+
);
7982
return toolchain;
8083
})
8184
.catch(error => {
8285
outputChannel.log("Failed to discover Swift toolchain");
8386
outputChannel.log(error);
8487
contextKeys.createNewProjectAvailable = false;
88+
contextKeys.switchPlatformAvailable = false;
8589
return undefined;
8690
});
8791

@@ -102,11 +106,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api> {
102106
event.affectsConfiguration("swift.SDK") ||
103107
event.affectsConfiguration("swift.swiftSDK")
104108
) {
105-
// FIXME: There is a bug stopping us from restarting SourceKit-LSP directly.
106-
// As long as it's fixed we won't need to reload on newer versions.
107-
showReloadExtensionNotification(
108-
"Changing the Swift SDK path requires the project be reloaded."
109-
);
109+
vscode.commands.executeCommand("swift.restartLSPServer");
110110
} else if (event.affectsConfiguration("swift.swiftEnvironmentVariables")) {
111111
showReloadExtensionNotification(
112112
"Changing environment variables requires the project be reloaded."
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2021-2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import { expect } from "chai";
16+
import * as vscode from "vscode";
17+
import {
18+
mockObject,
19+
mockGlobalObject,
20+
mockGlobalModule,
21+
MockedObject,
22+
mockFn,
23+
instance,
24+
} from "../../MockUtils";
25+
import {
26+
DarwinCompatibleTarget,
27+
SwiftToolchain,
28+
getDarwinTargetTriple,
29+
} from "../../../src/toolchain/toolchain";
30+
import { WorkspaceContext } from "../../../src/WorkspaceContext";
31+
import { switchPlatform } from "../../../src/commands/switchPlatform";
32+
import { StatusItem } from "../../../src/ui/StatusItem";
33+
import configuration from "../../../src/configuration";
34+
35+
suite("Switch Target Platform Unit Tests", () => {
36+
const mockedConfiguration = mockGlobalModule(configuration);
37+
const windowMock = mockGlobalObject(vscode, "window");
38+
const mockSwiftToolchain = mockGlobalModule(SwiftToolchain);
39+
let mockContext: MockedObject<WorkspaceContext>;
40+
let mockedStatusItem: MockedObject<StatusItem>;
41+
42+
setup(() => {
43+
mockedStatusItem = mockObject<StatusItem>({
44+
start: mockFn(),
45+
end: mockFn(),
46+
});
47+
mockContext = mockObject<WorkspaceContext>({
48+
statusItem: instance(mockedStatusItem),
49+
});
50+
});
51+
52+
test("Call Switch Platform and switch to iOS", async () => {
53+
const selectedItem = { value: DarwinCompatibleTarget.iOS, label: "iOS" };
54+
windowMock.showQuickPick.resolves(selectedItem);
55+
mockSwiftToolchain.getSDKForTarget.resolves("");
56+
expect(mockedConfiguration.swiftSDK).to.equal("");
57+
58+
await switchPlatform(instance(mockContext));
59+
60+
expect(windowMock.showQuickPick).to.have.been.calledOnce;
61+
expect(windowMock.showWarningMessage).to.have.been.calledOnceWithExactly(
62+
"Selecting the iOS target platform will provide code editing support, but compiling with a iOS SDK will have undefined results."
63+
);
64+
expect(mockedStatusItem.start).to.have.been.called;
65+
expect(mockedStatusItem.end).to.have.been.called;
66+
expect(mockedConfiguration.swiftSDK).to.equal(
67+
getDarwinTargetTriple(DarwinCompatibleTarget.iOS)
68+
);
69+
});
70+
71+
test("Call Switch Platform and switch to macOS", async () => {
72+
const selectedItem = { value: undefined, label: "macOS" };
73+
windowMock.showQuickPick.resolves(selectedItem);
74+
mockSwiftToolchain.getSDKForTarget.resolves("");
75+
expect(mockedConfiguration.swiftSDK).to.equal("");
76+
77+
await switchPlatform(instance(mockContext));
78+
79+
expect(windowMock.showQuickPick).to.have.been.calledOnce;
80+
expect(windowMock.showWarningMessage).to.not.have.been.called;
81+
expect(mockedStatusItem.start).to.have.been.called;
82+
expect(mockedStatusItem.end).to.have.been.called;
83+
expect(mockedConfiguration.swiftSDK).to.equal("");
84+
});
85+
});

0 commit comments

Comments
 (0)