Skip to content
66 changes: 56 additions & 10 deletions Sources/PackageModel/UserToolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public final class UserToolchain: Toolchain {
/// Search paths from the PATH environment variable.
let envSearchPaths: [AbsolutePath]

/// Only use search paths, do not fall back to `xcrun`.
/// Only use search paths, do not fall back to `xcrun` or `vswhere`.
let useXcrun: Bool
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still undecided if we want to rename the variable. I didn't do this at the point because neither have I got a short and clear name (useExternalTool sounds verbose and confusing), nor am I going to make vswhere the default like xcrun is now. It can be prioritized in later pitches.


private var _clangCompiler: AbsolutePath?
Expand Down Expand Up @@ -100,7 +100,7 @@ public final class UserToolchain: Toolchain {
return toolPath
}

private static func findTool(_ name: String, envSearchPaths: [AbsolutePath], useXcrun: Bool)
private static func findTool(_ name: String, destination: Destination, envSearchPaths: [AbsolutePath], swiftCompilerPath: AbsolutePath?, useXcrun: Bool)
throws -> AbsolutePath
{
if useXcrun {
Expand All @@ -117,15 +117,60 @@ public final class UserToolchain: Toolchain {
return toolPath
}
}

if useXcrun {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to introduce this approach as a fall-back from the beginning, and then makes it the default (like Clang does) if everything is working as expected.

#if os(Windows)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is #if os(Windows) check enough, or do we need to gate on host triple too?

func getHostTriple(from destination: Destination, swiftCompilerPath: AbsolutePath?) -> Triple? {
if let triple = destination.hostTriple {
return triple
} else if let swiftCompilerPath = swiftCompilerPath {
return .getHostTriple(usingSwiftCompiler: swiftCompilerPath)
} else {
return nil
}
}
if let hostTriple = getHostTriple(from: destination, swiftCompilerPath: swiftCompilerPath),
case let targetTriple = destination.targetTriple ?? hostTriple, targetTriple.isWindows() {
func vcArchNames(triple: Triple) -> (product: String, host: String, target: String)? {
switch triple.arch {
case .x86_64:
return ("Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "HostX64", "x64")
case .i686:
return ("Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "HostX86", "x86")
case .arm64, .arm64e, .aarch64:
return ("Microsoft.VisualStudio.Component.VC.Tools.ARM64", "HostARM64", "arm64")
case .arm, .armv5, .armv6, .armv7:
return ("Microsoft.VisualStudio.Component.VC.Tools.ARM", "HostARM", "arm")
default: return nil
}
}
if let (_, vcHost, _) = vcArchNames(triple: hostTriple),
let (vcTools, _, vcTarget) = vcArchNames(triple: targetTriple),
let programFiles = TSCBasic.ProcessEnv.vars["ProgramFiles(x86)"] {
if let visualStudio = try? TSCBasic.Process.checkNonZeroExit(arguments: [
"\(programFiles)\\Microsoft Visual Studio\\Installer\\vswhere.exe",
"-latest", "-products", "*", "-requires", vcTools, "-property", "installationPath"
]).spm_chomp(),
let vcToolsVersion = try? String(contentsOfFile: "\(visualStudio)\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt").spm_chomp(),
let vcToolsDir = try? AbsolutePath(validating: "\(visualStudio)\\VC\\Tools\\MSVC\\\(vcToolsVersion)\\bin\\\(vcHost)\\\(vcTarget)"),
let toolPath = try? getTool(name, binDir: vcToolsDir) {
return toolPath
}
}
}
#endif
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the behaviour different when targeting Windows from Windows and Windows from another environment just making things more complicated and confusing for users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what useXcrun is already doing for Darwin to Darwin case, which I believe is reasonable — just like xcrun is only available on macOS, Visual Studio is basically a Windows thing.


throw InvalidToolchainDiagnostic("could not find \(name)")
}

// MARK: - public API

public static func determineLibrarian(
triple: Triple, binDir: AbsolutePath,
triple: Triple, destination: Destination, binDir: AbsolutePath,
useXcrun: Bool,
environment: EnvironmentVariables,
swiftCompilerPath: AbsolutePath,
searchPaths: [AbsolutePath]
) throws
-> AbsolutePath
Expand Down Expand Up @@ -163,12 +208,12 @@ public final class UserToolchain: Toolchain {
if let librarian = try? UserToolchain.getTool(tool, binDir: binDir) {
return librarian
}
return try UserToolchain.findTool(tool, envSearchPaths: searchPaths, useXcrun: useXcrun)
return try UserToolchain.findTool(tool, destination: destination, envSearchPaths: searchPaths, swiftCompilerPath: swiftCompilerPath, useXcrun: useXcrun)
}

/// Determines the Swift compiler paths for compilation and manifest parsing.
public static func determineSwiftCompilers(
binDir: AbsolutePath, useXcrun: Bool, environment: EnvironmentVariables,
binDir: AbsolutePath, destination: Destination, useXcrun: Bool, environment: EnvironmentVariables,
searchPaths: [AbsolutePath]
) throws -> SwiftCompilers {
func validateCompiler(at path: AbsolutePath?) throws {
Expand Down Expand Up @@ -201,7 +246,7 @@ public final class UserToolchain: Toolchain {
// Try to lookup swift compiler on the system which is possible when
// we're built outside of the Swift toolchain.
resolvedBinDirCompiler = try UserToolchain.findTool(
"swiftc", envSearchPaths: searchPaths, useXcrun: useXcrun)
"swiftc", destination: destination, envSearchPaths: searchPaths, swiftCompilerPath: nil, useXcrun: useXcrun)
}

// The compiler for compilation tasks is SWIFT_EXEC or the bin dir compiler.
Expand Down Expand Up @@ -236,7 +281,7 @@ public final class UserToolchain: Toolchain {

// Otherwise, lookup it up on the system.
let toolPath = try UserToolchain.findTool(
"clang", envSearchPaths: self.envSearchPaths, useXcrun: useXcrun)
"clang", destination: destination, envSearchPaths: self.envSearchPaths, swiftCompilerPath: swiftCompilerPath, useXcrun: useXcrun)
self._clangCompiler = toolPath
return toolPath
}
Expand All @@ -261,7 +306,7 @@ public final class UserToolchain: Toolchain {
}
// If that fails, fall back to xcrun, PATH, etc.
return try UserToolchain.findTool(
"lldb", envSearchPaths: self.envSearchPaths, useXcrun: useXcrun)
"lldb", destination: destination, envSearchPaths: self.envSearchPaths, swiftCompilerPath: swiftCompilerPath, useXcrun: useXcrun)
}

/// Returns the path to llvm-cov tool.
Expand Down Expand Up @@ -430,7 +475,7 @@ public final class UserToolchain: Toolchain {
let binDir = destination.toolchainBinDir

let swiftCompilers = try UserToolchain.determineSwiftCompilers(
binDir: binDir, useXcrun: useXcrun, environment: environment, searchPaths: envSearchPaths)
binDir: binDir, destination: destination, useXcrun: useXcrun, environment: environment, searchPaths: envSearchPaths)
self.swiftCompilerPath = swiftCompilers.compile
self.architectures = destination.architectures

Expand All @@ -439,7 +484,8 @@ public final class UserToolchain: Toolchain {
destination.targetTriple ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile)

self.librarianPath = try UserToolchain.determineLibrarian(
triple: triple, binDir: binDir, useXcrun: useXcrun, environment: environment,
triple: triple, destination: destination, binDir: binDir,
useXcrun: useXcrun, environment: environment, swiftCompilerPath: swiftCompilerPath,
searchPaths: envSearchPaths)

// Change the triple to the specified arch if there's exactly one of them.
Expand Down