diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5bd28e0..27f3be2d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Make sure activation does not fail when `swift.path` is `null` ([#1616](https://github.com/swiftlang/vscode-swift/pull/1616)) - Fix `Swift: Reset Package Dependencies` command on Windows ([#1614](https://github.com/swiftlang/vscode-swift/pull/1614)) - Activate extension when a .swift source file exists in a subfolder ([#1635](https://github.com/swiftlang/vscode-swift/pull/1635)) +- Resolve Swiftly toolchain path ([#1632](https://github.com/swiftlang/vscode-swift/pull/1632)) ## 2.4.0 - 2025-06-11 diff --git a/src/toolchain/toolchain.ts b/src/toolchain/toolchain.ts index 7eb9815f2..f974b1310 100644 --- a/src/toolchain/toolchain.ts +++ b/src/toolchain/toolchain.ts @@ -20,7 +20,7 @@ import * as vscode from "vscode"; import configuration from "../configuration"; import { SwiftOutputChannel } from "../ui/SwiftOutputChannel"; import { execFile, ExecFileError, execSwift } from "../utilities/utilities"; -import { expandFilePathTilde, pathExists } from "../utilities/filesystem"; +import { expandFilePathTilde, fileExists, pathExists } from "../utilities/filesystem"; import { Version } from "../utilities/version"; import { BuildFlags } from "./BuildFlags"; import { Sanitizer } from "./Sanitizer"; @@ -119,7 +119,7 @@ export class SwiftToolchain { } static async create(folder?: vscode.Uri): Promise { - const swiftFolderPath = await this.getSwiftFolderPath(); + const swiftFolderPath = await this.getSwiftFolderPath(folder); const toolchainPath = await this.getToolchainPath(swiftFolderPath, folder); const targetInfo = await this.getSwiftTargetInfo( this._getToolchainExecutable(toolchainPath, "swift") @@ -562,7 +562,7 @@ export class SwiftToolchain { channel.logDiagnostic(this.diagnostics); } - private static async getSwiftFolderPath(): Promise { + private static async getSwiftFolderPath(cwd?: vscode.Uri): Promise { try { let swift: string; if (configuration.path !== "") { @@ -605,7 +605,17 @@ export class SwiftToolchain { } } // swift may be a symbolic link - const realSwift = await fs.realpath(swift); + let realSwift = await fs.realpath(swift); + if (path.basename(realSwift) === "swiftly") { + try { + const inUse = await this.swiftlyInUseLocation(realSwift, cwd); + if (inUse) { + realSwift = path.join(inUse, "usr", "bin", "swift"); + } + } catch { + // Ignore, will fall back to original path + } + } const swiftPath = expandFilePathTilde(path.dirname(realSwift)); return await this.getSwiftEnvPath(swiftPath); } catch { @@ -645,11 +655,23 @@ export class SwiftToolchain { try { switch (process.platform) { case "darwin": { - if (configuration.path !== "") { + const configPath = configuration.path; + if (configPath !== "") { + const swiftlyPath = path.join(configPath, "swiftly"); + if (await fileExists(swiftlyPath)) { + try { + const inUse = await this.swiftlyInUseLocation(swiftlyPath, cwd); + if (inUse) { + return path.join(inUse, "usr"); + } + } catch { + // Ignore, will fall back to original path + } + } return path.dirname(configuration.path); } - const swiftlyToolchainLocation = await this.swiftlyToolchainLocation(cwd); + const swiftlyToolchainLocation = await this.swiftlyToolchain(cwd); if (swiftlyToolchainLocation) { return swiftlyToolchainLocation; } @@ -669,12 +691,19 @@ export class SwiftToolchain { } } + private static async swiftlyInUseLocation(swiftlyPath: string, cwd?: vscode.Uri) { + const { stdout: inUse } = await execFile(swiftlyPath, ["use", "--print-location"], { + cwd: cwd?.fsPath, + }); + return inUse.trimEnd(); + } + /** * Determine if Swiftly is being used to manage the active toolchain and if so, return * the path to the active toolchain. * @returns The location of the active toolchain if swiftly is being used to manage it. */ - private static async swiftlyToolchainLocation(cwd?: vscode.Uri): Promise { + private static async swiftlyToolchain(cwd?: vscode.Uri): Promise { const swiftlyHomeDir: string | undefined = process.env["SWIFTLY_HOME_DIR"]; if (swiftlyHomeDir) { const { stdout: swiftLocation } = await execFile("which", ["swift"]); @@ -683,17 +712,9 @@ export class SwiftToolchain { // is no cwd specified then it returns the global "inUse" toolchain otherwise // it respects the .swift-version file in the cwd and resolves using that. try { - const { stdout: swiftlyLocation } = await execFile( - "swiftly", - ["use", "--print-location"], - { - cwd: cwd?.fsPath, - } - ); - - const trimmedLocation = swiftlyLocation.trimEnd(); - if (trimmedLocation.length > 0) { - return path.join(trimmedLocation, "usr"); + const inUse = await this.swiftlyInUseLocation("swiftly", cwd); + if (inUse.length > 0) { + return path.join(inUse, "usr"); } } catch (err: unknown) { const error = err as ExecFileError;