Skip to content

Commit e7c96bc

Browse files
committed
Add quick switcher between iOS and macOS targets
1 parent c2c4fe4 commit e7c96bc

File tree

7 files changed

+151
-2
lines changed

7 files changed

+151
-2
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
"title": "Clean Build",
4545
"category": "Swift"
4646
},
47+
{
48+
"command": "swift.quickSwitchTarget",
49+
"title": "Switch Target",
50+
"category": "Swift"
51+
},
4752
{
4853
"command": "swift.resetPackage",
4954
"title": "Reset Package Dependencies",
@@ -245,6 +250,10 @@
245250
"command": "swift.cleanBuild",
246251
"when": "swift.hasPackage"
247252
},
253+
{
254+
"command": "swift.quickSwitchTarget",
255+
"when": "isMac"
256+
},
248257
{
249258
"command": "swift.resetPackage",
250259
"when": "swift.hasPackage"

src/commands.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ import { FolderEvent, WorkspaceContext } from "./WorkspaceContext";
1919
import { createSwiftTask, SwiftTaskProvider } from "./SwiftTaskProvider";
2020
import { FolderContext } from "./FolderContext";
2121
import { PackageNode } from "./ui/PackageDependencyProvider";
22+
import { withQuickPick } from "./ui/QuickPick";
2223
import { execSwift } from "./utilities/utilities";
2324
import { Version } from "./utilities/version";
25+
import { DarwinCompatibleTarget, SwiftToolchain } from "./toolchain/toolchain";
26+
import configuration from "./configuration";
2427

2528
/**
2629
* References:
@@ -428,6 +431,34 @@ function openInExternalEditor(packageNode: PackageNode) {
428431
}
429432
}
430433

434+
interface IDawrinCompatibleTarget extends vscode.QuickPickItem {
435+
value: DarwinCompatibleTarget;
436+
label: string;
437+
}
438+
439+
/**
440+
* Switches the target SDK to the platform selected in a QuickPick UI.
441+
*/
442+
async function switchPlatform() {
443+
const onSelect = async (picked: IDawrinCompatibleTarget) => {
444+
const sdkForTarget = await SwiftToolchain.getSdkForTarget(picked.value);
445+
if (sdkForTarget) {
446+
configuration.sdk = sdkForTarget;
447+
} else {
448+
console.warn("unable to obtain new SDK path");
449+
}
450+
};
451+
452+
await withQuickPick<IDawrinCompatibleTarget>(
453+
"Select a new target",
454+
[
455+
{ value: DarwinCompatibleTarget.macOS, label: "macOS" },
456+
{ value: DarwinCompatibleTarget.iOS, label: "iOS" },
457+
],
458+
onSelect
459+
);
460+
}
461+
431462
function updateAfterError(result: boolean, folderContext: FolderContext) {
432463
const triggerResolvedUpdatedEvent = folderContext.hasResolveErrors;
433464
// set has resolve errors flag
@@ -450,6 +481,8 @@ export function register(ctx: WorkspaceContext) {
450481
),
451482
vscode.commands.registerCommand("swift.updateDependencies", () => updateDependencies(ctx)),
452483
vscode.commands.registerCommand("swift.cleanBuild", () => cleanBuild(ctx)),
484+
// This is only available on macOS (gated in `package.json`) because its the only OS that has the iOS SDK available.
485+
vscode.commands.registerCommand("swift.quickSwitchTarget", () => switchPlatform()),
453486
vscode.commands.registerCommand("swift.resetPackage", () => resetPackage(ctx)),
454487
vscode.commands.registerCommand("swift.runSingle", () => runSingleFile(ctx)),
455488
vscode.commands.registerCommand("swift.openPackage", () => openPackage(ctx)),

src/configuration.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ const configuration = {
7373
get sdk(): string {
7474
return vscode.workspace.getConfiguration("swift").get<string>("SDK", "");
7575
},
76+
set sdk(value: string) {
77+
vscode.workspace.getConfiguration("swift").update("SDK", value);
78+
},
7679
/** swift build arguments */
7780
get buildArguments(): string[] {
7881
return vscode.workspace.getConfiguration("swift").get<string[]>("buildArguments", []);

src/sourcekit-lsp/LanguageClientManager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
swiftDriverSDKFlags,
2424
buildPathFlags,
2525
swiftRuntimeEnv,
26+
swiftDriverTargetFlags,
2627
} from "../utilities/utilities";
2728
import { Version } from "../utilities/version";
2829
import { FolderEvent, WorkspaceContext } from "../WorkspaceContext";
@@ -345,6 +346,7 @@ export class LanguageClientManager {
345346
serverPathConfig.length > 0 ? serverPathConfig : getSwiftExecutable("sourcekit-lsp");
346347
const sdkArguments = [
347348
...swiftDriverSDKFlags(true),
349+
...swiftDriverTargetFlags(true),
348350
...filterArguments(
349351
configuration.buildArguments.concat(buildPathFlags()),
350352
LanguageClientManager.buildArgumentFilter

src/toolchain/toolchain.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ interface SwiftTargetInfo {
4646
[name: string]: string | object | undefined;
4747
}
4848

49+
export enum DarwinCompatibleTarget {
50+
iOS,
51+
macOS,
52+
}
53+
4954
export class SwiftToolchain {
5055
constructor(
5156
public swiftFolderPath: string,
@@ -195,8 +200,8 @@ export class SwiftToolchain {
195200
if (process.env.SDKROOT) {
196201
return process.env.SDKROOT;
197202
}
198-
const { stdout } = await execFile("xcrun", ["--sdk", "macosx", "--show-sdk-path"]);
199-
return path.join(stdout.trimEnd());
203+
204+
return this.getSdkForTarget(DarwinCompatibleTarget.macOS);
200205
}
201206
case "win32": {
202207
return process.env.SDKROOT;
@@ -205,6 +210,27 @@ export class SwiftToolchain {
205210
return undefined;
206211
}
207212

213+
/**
214+
* @param target Target to obtain the SDK path for
215+
* @returns path to the SDK for the target
216+
*/
217+
public static async getSdkForTarget(
218+
target: DarwinCompatibleTarget
219+
): Promise<string | undefined> {
220+
let sdkType: string;
221+
switch (target) {
222+
case DarwinCompatibleTarget.macOS:
223+
sdkType = "macosx";
224+
break;
225+
case DarwinCompatibleTarget.iOS:
226+
sdkType = "iphoneos";
227+
break;
228+
}
229+
230+
const { stdout } = await execFile("xcrun", ["--sdk", sdkType, "--show-sdk-path"]);
231+
return path.join(stdout.trimEnd());
232+
}
233+
208234
/**
209235
* @returns path to custom SDK
210236
*/

src/ui/QuickPick.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VSCode Swift open source project
4+
//
5+
// Copyright (c) 2021 the VSCode 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 VSCode Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
17+
/**
18+
* Displays a QuickPick UI with the parameters and passes the result
19+
* to the provided closure.
20+
*/
21+
export async function withQuickPick<T extends vscode.QuickPickItem>(
22+
placeholder: string,
23+
options: T[],
24+
onSelect: (picked: T) => Promise<void>
25+
) {
26+
const picker = vscode.window.createQuickPick<T>();
27+
28+
picker.placeholder = placeholder;
29+
picker.items = options;
30+
31+
picker.show();
32+
33+
const pickedItem = await new Promise<T | undefined>(resolve => {
34+
picker.onDidAccept(() => resolve(picker.selectedItems[0]));
35+
picker.onDidHide(() => resolve(undefined));
36+
});
37+
38+
picker.busy = true;
39+
40+
if (pickedItem) {
41+
await onSelect(pickedItem);
42+
picker.busy = false;
43+
}
44+
45+
picker.dispose();
46+
}

src/utilities/utilities.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,36 @@ export function swiftDriverSDKFlags(indirect = false): string[] {
251251
return indirect ? args.flatMap(arg => ["-Xswiftc", arg]) : args;
252252
}
253253

254+
/**
255+
* Get target flags for swiftc
256+
*
257+
* @param indirect whether to pass the flags by -Xswiftc
258+
*/
259+
export function swiftDriverTargetFlags(indirect = false): string[] {
260+
const IPHONE_SDK_KIND = "iPhoneOS";
261+
262+
let args: string[] = [];
263+
264+
if (configuration.sdk === "") {
265+
return args;
266+
}
267+
268+
const sdkKindParts = configuration.sdk.split("/");
269+
const sdkKind = sdkKindParts[sdkKindParts.length - 1];
270+
if (sdkKind.includes(IPHONE_SDK_KIND)) {
271+
// Obtain the iOS version of the SDK.
272+
const version = sdkKind.substring(
273+
// Trim the prefix
274+
sdkKind.length - IPHONE_SDK_KIND.length,
275+
// Trim the `.sdk` suffix
276+
sdkKind.length - 4
277+
);
278+
args = ["-target", `arm64-apple-ios${version}`];
279+
}
280+
281+
return indirect ? args.flatMap(arg => ["-Xswiftc", arg]) : args;
282+
}
283+
254284
/**
255285
* Get the file name of executable
256286
*

0 commit comments

Comments
 (0)