From 22a1680940dccdb65299fc05d907e14ed6d90df0 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 9 Aug 2022 17:18:04 +0100 Subject: [PATCH 1/4] Add quick pick to select Xcode toolchain to use --- package.json | 19 +++++++++++---- src/commands.ts | 49 ++++++++++++++++++++++++++++++++++++-- src/configuration.ts | 3 +++ src/toolchain/toolchain.ts | 23 ++++++++++++++++-- 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 9ebf6231d..664bdeec2 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,6 @@ "title": "Clean Build", "category": "Swift" }, - { - "command": "swift.switchPlatform", - "title": "Switch Target", - "category": "Swift" - }, { "command": "swift.resetPackage", "title": "Reset Package Dependencies", @@ -89,6 +84,16 @@ "command": "swift.openPackage", "title": "Open Package.swift", "category": "Swift" + }, + { + "command": "swift.switchPlatform", + "title": "Select Target Platform", + "category": "Swift" + }, + { + "command": "swift.selectXcodeDeveloperDir", + "title": "Select Xcode Developer Dir", + "category": "Swift" } ], "configuration": [ @@ -276,6 +281,10 @@ { "command": "swift.openExternal", "when": "false" + }, + { + "command": "swift.selectXcodeDeveloperDir", + "when": "isMac" } ], "view/title": [ diff --git a/src/commands.ts b/src/commands.ts index 521c484dc..5a420f050 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -15,6 +15,7 @@ import * as vscode from "vscode"; import * as fs from "fs/promises"; import * as path from "path"; +import configuration from "./configuration"; import { FolderEvent, WorkspaceContext } from "./WorkspaceContext"; import { createSwiftTask, SwiftTaskProvider } from "./SwiftTaskProvider"; import { FolderContext } from "./FolderContext"; @@ -23,7 +24,6 @@ import { withQuickPick } from "./ui/QuickPick"; import { execSwift } from "./utilities/utilities"; import { Version } from "./utilities/version"; import { DarwinCompatibleTarget, SwiftToolchain } from "./toolchain/toolchain"; -import configuration from "./configuration"; /** * References: @@ -459,6 +459,48 @@ async function switchPlatform() { ); } +/** + * Choose DEVELOPER_DIR + * @param workspaceContext + */ +async function selectXcodeDeveloperDir() { + const defaultXcode = await SwiftToolchain.getXcodeDeveloperDir(); + const selectedXcode = configuration.swiftEnvironmentVariables.DEVELOPER_DIR; + const xcodes = await SwiftToolchain.getXcodeInstalls(); + await withQuickPick( + selectedXcode ?? defaultXcode, + xcodes.map(xcode => { + const developerDir = `${xcode}/Contents/Developer`; + return { + label: developerDir === defaultXcode ? `${xcode} (default)` : xcode, + folder: developerDir === defaultXcode ? undefined : developerDir, + }; + }), + async selected => { + const swiftEnv = configuration.swiftEnvironmentVariables; + const previousDeveloperDir = swiftEnv.DEVELOPER_DIR ?? defaultXcode; + if (selected.folder) { + swiftEnv.DEVELOPER_DIR = selected.folder; + } else if (swiftEnv.DEVELOPER_DIR) { + // if DEVELOPER_DIR was set and the new folder is the default then + // delete variable + delete swiftEnv.DEVELOPER_DIR; + } + configuration.swiftEnvironmentVariables = swiftEnv; + // if SDK is inside previous DEVELOPER_DIR then move to new DEVELOPER_DIR + if ( + configuration.sdk.length > 0 && + configuration.sdk.startsWith(previousDeveloperDir) + ) { + configuration.sdk = configuration.sdk.replace( + previousDeveloperDir, + selected.folder ?? defaultXcode + ); + } + } + ); +} + function updateAfterError(result: boolean, folderContext: FolderContext) { const triggerResolvedUpdatedEvent = folderContext.hasResolveErrors; // set has resolve errors flag @@ -510,6 +552,9 @@ export function register(ctx: WorkspaceContext) { if (item instanceof PackageNode) { openInExternalEditor(item); } - }) + }), + vscode.commands.registerCommand("swift.selectXcodeDeveloperDir", () => + selectXcodeDeveloperDir() + ) ); } diff --git a/src/configuration.ts b/src/configuration.ts index 2c27ad6f5..22f4ae545 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -89,6 +89,9 @@ const configuration = { .getConfiguration("swift") .get<{ [key: string]: string }>("swiftEnvironmentVariables", {}); }, + set swiftEnvironmentVariables(vars: { [key: string]: string }) { + vscode.workspace.getConfiguration("swift").update("swiftEnvironmentVariables", vars); + }, /** include build errors in problems view */ get problemMatchCompileErrors(): boolean { return vscode.workspace diff --git a/src/toolchain/toolchain.ts b/src/toolchain/toolchain.ts index 39df3bfd2..5a1388e91 100644 --- a/src/toolchain/toolchain.ts +++ b/src/toolchain/toolchain.ts @@ -95,6 +95,25 @@ export class SwiftToolchain { ); } + /** + * Get active developer dir for Xcode + */ + public static async getXcodeDeveloperDir(): Promise { + const { stdout } = await execFile("xcode-select", ["-p"]); + return stdout.trimEnd(); + } + + /** + * Get list of Xcode versions intalled on mac + * @returns Folders for each Xcode install + */ + public static async getXcodeInstalls(): Promise { + const { stdout: xcodes } = await execFile("mdfind", [ + `kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'`, + ]); + return xcodes.trimEnd().split("\n"); + } + logDiagnostics(channel: SwiftOutputChannel) { channel.logDiagnostic(`Swift Path: ${this.swiftFolderPath}`); channel.logDiagnostic(`Toolchain Path: ${this.toolchainPath}`); @@ -260,8 +279,8 @@ export class SwiftToolchain { ): Promise { switch (process.platform) { case "darwin": { - const { stdout } = await execFile("xcode-select", ["-p"]); - return path.join(stdout.trimEnd(), "usr", "bin"); + const developerDir = await this.getXcodeDeveloperDir(); + return path.join(developerDir, "usr", "bin"); } case "win32": { // look up runtime library directory for XCTest alternatively From a25b8088a8bdd1801f3fb416efcaf993519d0476 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 9 Aug 2022 17:54:11 +0100 Subject: [PATCH 2/4] Fix deleting DEVELOPER_DIR key --- src/commands.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 5a420f050..7b6210b27 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -477,14 +477,16 @@ async function selectXcodeDeveloperDir() { }; }), async selected => { - const swiftEnv = configuration.swiftEnvironmentVariables; + let swiftEnv = configuration.swiftEnvironmentVariables; const previousDeveloperDir = swiftEnv.DEVELOPER_DIR ?? defaultXcode; if (selected.folder) { swiftEnv.DEVELOPER_DIR = selected.folder; } else if (swiftEnv.DEVELOPER_DIR) { // if DEVELOPER_DIR was set and the new folder is the default then // delete variable - delete swiftEnv.DEVELOPER_DIR; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { DEVELOPER_DIR, ...rest } = swiftEnv; + swiftEnv = rest; } configuration.swiftEnvironmentVariables = swiftEnv; // if SDK is inside previous DEVELOPER_DIR then move to new DEVELOPER_DIR From 993cfe4854c44a3b3b1c04f29369bd563103ed18 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 9 Aug 2022 17:54:31 +0100 Subject: [PATCH 3/4] Resolved tasks should use same environment --- src/SwiftTaskProvider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SwiftTaskProvider.ts b/src/SwiftTaskProvider.ts index 4f90d35c2..aeb7a2360 100644 --- a/src/SwiftTaskProvider.ts +++ b/src/SwiftTaskProvider.ts @@ -294,6 +294,7 @@ export class SwiftTaskProvider implements vscode.TaskProvider { "swift", new vscode.ShellExecution(swift, task.definition.args, { cwd: fullCwd, + env: { ...configuration.swiftEnvironmentVariables, ...swiftRuntimeEnv() }, }), task.problemMatchers ); From 100dc8d0207af3653430c1c899a1baff6b068333 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 14 Aug 2022 17:05:00 +0100 Subject: [PATCH 4/4] Remove DarwinQuickPickTarget. We don't need it --- src/commands.ts | 25 +++++++------------ src/toolchain/toolchain.ts | 49 +++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 7b6210b27..5bd169839 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -431,31 +431,24 @@ function openInExternalEditor(packageNode: PackageNode) { } } -interface DarwinQuickPickTarget extends vscode.QuickPickItem { - value: DarwinCompatibleTarget; - label: string; -} - /** * Switches the target SDK to the platform selected in a QuickPick UI. */ async function switchPlatform() { - const onSelect = async (picked: DarwinQuickPickTarget) => { - const sdkForTarget = await SwiftToolchain.getSdkForTarget(picked.value); - if (sdkForTarget) { - configuration.sdk = sdkForTarget; - } else { - vscode.window.showErrorMessage("Unable to obtain requested SDK path"); - } - }; - - await withQuickPick( + await withQuickPick( "Select a new target", [ { value: DarwinCompatibleTarget.macOS, label: "macOS" }, { value: DarwinCompatibleTarget.iOS, label: "iOS" }, ], - onSelect + async picked => { + const sdkForTarget = await SwiftToolchain.getSdkForTarget(picked.value); + if (sdkForTarget) { + configuration.sdk = sdkForTarget; + } else { + vscode.window.showErrorMessage("Unable to obtain requested SDK path"); + } + } ); } diff --git a/src/toolchain/toolchain.ts b/src/toolchain/toolchain.ts index 5a1388e91..6261bec1f 100644 --- a/src/toolchain/toolchain.ts +++ b/src/toolchain/toolchain.ts @@ -14,6 +14,7 @@ import * as fs from "fs/promises"; import * as path from "path"; +import { normalize } from "path"; import * as plist from "plist"; import * as vscode from "vscode"; import configuration from "../configuration"; @@ -103,6 +104,30 @@ export class SwiftToolchain { return stdout.trimEnd(); } + /** + * @param target Target to obtain the SDK path for + * @returns path to the SDK for the target + */ + public static async getSdkForTarget( + target: DarwinCompatibleTarget + ): Promise { + let sdkType: string; + switch (target) { + case DarwinCompatibleTarget.macOS: + // macOS is the default target, so lets not update the SDK + return undefined; + case DarwinCompatibleTarget.iOS: + sdkType = "iphoneos"; + break; + } + + // Include custom variables so that non-standard XCode installs can be better supported. + const { stdout } = await execFile("xcrun", ["--sdk", sdkType, "--show-sdk-path"], { + env: { ...process.env, ...configuration.swiftEnvironmentVariables }, + }); + return path.join(stdout.trimEnd()); + } + /** * Get list of Xcode versions intalled on mac * @returns Folders for each Xcode install @@ -233,30 +258,6 @@ export class SwiftToolchain { return undefined; } - /** - * @param target Target to obtain the SDK path for - * @returns path to the SDK for the target - */ - public static async getSdkForTarget( - target: DarwinCompatibleTarget - ): Promise { - let sdkType: string; - switch (target) { - case DarwinCompatibleTarget.macOS: - sdkType = "macosx"; - break; - case DarwinCompatibleTarget.iOS: - sdkType = "iphoneos"; - break; - } - - // Include custom variables so that non-standard XCode installs can be better supported. - const { stdout } = await execFile("xcrun", ["--sdk", sdkType, "--show-sdk-path"], { - env: { ...process.env, ...configuration.swiftEnvironmentVariables }, - }); - return path.join(stdout.trimEnd()); - } - /** * @returns path to custom SDK */