Skip to content

Commit 2d7b2bc

Browse files
authored
refactor toolchain SwiftPM libraries path (#3660)
motivation: improve logic in how Manifest and Plugin APIs locations are derived changes: * rename fields on ToolchainConfiguration to better articulate their meaning * change logic in UserToolchain to derive the SwiftPM libraries path more clearly and reliably * adjust call-sites and test
1 parent 739be7e commit 2d7b2bc

File tree

15 files changed

+253
-151
lines changed

15 files changed

+253
-151
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ $> SWIFT_EXEC=/path/to/my/built/swiftc swift build
344344
345345
SwiftPM computes the path of its runtime libraries relative to where it is
346346
installed. This path can be overridden by setting the environment variable
347-
`SWIFTPM_PD_LIBS` to a directory containing the libraries, or a colon-separated list of
347+
`SWIFTPM_CUSTOM_LIBS_DIR` to a directory containing the libraries, or a colon-separated list of
348348
absolute search paths. SwiftPM will choose the first
349349
path which exists on disk. If none of the paths are present on disk, it will fall
350350
back to built-in computation.

Sources/Commands/SwiftRunTool.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public struct SwiftRunTool: SwiftCommand {
136136
let arguments = buildOp.buildPlan!.createREPLArguments()
137137
print("Launching Swift REPL with arguments: \(arguments.joined(separator: " "))")
138138
try run(
139-
swiftTool.getToolchain().swiftInterpreter,
139+
swiftTool.getToolchain().swiftInterpreterPath,
140140
originalWorkingDirectory: swiftTool.originalWorkingDirectory,
141141
arguments: arguments)
142142

@@ -171,7 +171,7 @@ public struct SwiftRunTool: SwiftCommand {
171171
if let executable = options.executable, isValidSwiftFilePath(executable) {
172172
swiftTool.diagnostics.emit(.runFileDeprecation)
173173
// Redirect execution to the toolchain's swift executable.
174-
let swiftInterpreterPath = try swiftTool.getToolchain().swiftInterpreter
174+
let swiftInterpreterPath = try swiftTool.getToolchain().swiftInterpreterPath
175175
// Prepend the script to interpret to the arguments.
176176
let arguments = [executable] + options.arguments
177177
try run(

Sources/Commands/SwiftTestTool.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,10 +1039,9 @@ fileprivate func constructTestEnvironment(
10391039
let codecovProfile = buildParameters.buildPath.appending(components: "codecov", "default%m.profraw")
10401040
env["LLVM_PROFILE_FILE"] = codecovProfile.pathString
10411041
}
1042-
10431042
#if !os(macOS)
10441043
#if os(Windows)
1045-
if let location = toolchain.manifestResources.xctestLocation {
1044+
if let location = toolchain.configuration.xctestPath {
10461045
env["Path"] = "\(location.pathString);\(env["Path"] ?? "")"
10471046
}
10481047
#endif

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -689,21 +689,20 @@ public final class ManifestLoader: ManifestLoaderProtocol {
689689
let moduleCachePath = (ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]).flatMap{ AbsolutePath.init($0) }
690690

691691
var cmd: [String] = []
692-
cmd += [self.toolchain.swiftCompiler.pathString]
692+
cmd += [self.toolchain.swiftCompilerPath.pathString]
693693
cmd += verbosity.ccArgs
694694

695695
let macOSPackageDescriptionPath: AbsolutePath
696-
// If we got the binDir that means we could be developing SwiftPM in Xcode
696+
// if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode
697697
// which produces a framework for dynamic package products.
698-
let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks")
699-
if self.toolchain.binDir != nil, localFileSystem.exists(packageFrameworkPath) {
698+
if runtimePath.extension == "framework" {
700699
cmd += [
701-
"-F", packageFrameworkPath.pathString,
700+
"-F", runtimePath.parentDirectory.pathString,
702701
"-framework", "PackageDescription",
703-
"-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString,
702+
"-Xlinker", "-rpath", "-Xlinker", runtimePath.parentDirectory.pathString,
704703
]
705704

706-
macOSPackageDescriptionPath = packageFrameworkPath.appending(RelativePath("PackageDescription.framework/PackageDescription"))
705+
macOSPackageDescriptionPath = runtimePath.appending(component: "PackageDescription")
707706
} else {
708707
cmd += [
709708
"-L", runtimePath.pathString,
@@ -716,13 +715,13 @@ public final class ManifestLoader: ManifestLoaderProtocol {
716715
#endif
717716

718717
// note: this is not correct for all platforms, but we only actually use it on macOS.
719-
macOSPackageDescriptionPath = runtimePath.appending(RelativePath("libPackageDescription.dylib"))
718+
macOSPackageDescriptionPath = runtimePath.appending(component: "libPackageDescription.dylib")
720719
}
721720

722721
// Use the same minimum deployment target as the PackageDescription library (with a fallback of 10.15).
723722
#if os(macOS)
724723
let triple = Self._hostTriple.memoize {
725-
Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompiler)
724+
Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompilerPath)
726725
}
727726

728727
let version = try Self._packageDescriptionMinimumDeploymentTarget.memoize {
@@ -853,9 +852,15 @@ public final class ManifestLoader: ManifestLoaderProtocol {
853852
var cmd = [String]()
854853
let runtimePath = self.runtimePath(for: toolsVersion)
855854
cmd += ["-swift-version", toolsVersion.swiftLanguageVersion.rawValue]
856-
cmd += ["-I", runtimePath.pathString]
855+
// if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode
856+
// which produces a framework for dynamic package products.
857+
if runtimePath.extension == "framework" {
858+
cmd += ["-I", runtimePath.parentDirectory.parentDirectory.pathString]
859+
} else {
860+
cmd += ["-I", runtimePath.pathString]
861+
}
857862
#if os(macOS)
858-
if let sdkRoot = self.toolchain.sdkRoot ?? self.sdkRoot() {
863+
if let sdkRoot = self.toolchain.sdkRootPath ?? self.sdkRoot() {
859864
cmd += ["-sdk", sdkRoot.pathString]
860865
}
861866
#endif
@@ -865,19 +870,14 @@ public final class ManifestLoader: ManifestLoaderProtocol {
865870

866871
/// Returns the runtime path given the manifest version and path to libDir.
867872
private func runtimePath(for version: ToolsVersion) -> AbsolutePath {
868-
// Bin dir will be set when developing swiftpm without building all of the runtimes.
869-
if let binDir = self.toolchain.binDir {
870-
return binDir
871-
}
872-
873-
// Otherwise we use the standard location of the manifest API in the toolchain, if it exists.
874-
let manifestAPIDir = self.toolchain.libDir.appending(component: "ManifestAPI")
873+
let manifestAPIDir = self.toolchain.swiftPMLibrariesLocation.manifestAPI
875874
if localFileSystem.exists(manifestAPIDir) {
876875
return manifestAPIDir
877876
}
878-
879-
// Otherwise, fall back on the old location (this would indicate that we're using an old toolchain).
880-
return self.toolchain.libDir.appending(version.runtimeSubpath)
877+
878+
// FIXME: how do we test this?
879+
// Fall back on the old location (this would indicate that we're using an old toolchain).
880+
return self.toolchain.swiftPMLibrariesLocation.manifestAPI.parentDirectory.appending(version.runtimeSubpath)
881881
}
882882

883883
/// Returns path to the manifest database inside the given cache directory.

Sources/PackageModel/ToolchainConfiguration.swift

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,56 @@ import TSCBasic
1616
/// using the package manager with alternate toolchains in the future.
1717
public struct ToolchainConfiguration {
1818
/// The path of the swift compiler.
19-
public var swiftCompiler: AbsolutePath
19+
public var swiftCompilerPath: AbsolutePath
2020

2121
/// Extra arguments to pass the Swift compiler (defaults to the empty string).
2222
public var swiftCompilerFlags: [String]
2323

2424
/// Environment to pass to the Swift compiler (defaults to the inherited environment).
2525
public var swiftCompilerEnvironment: [String: String]
2626

27-
/// The path of the library resources.
28-
public var libDir: AbsolutePath
29-
30-
/// The bin directory.
31-
public var binDir: AbsolutePath?
27+
/// SwiftPM library paths.
28+
public var swiftPMLibrariesLocation: SwiftPMLibrariesLocation
3229

3330
/// The path to SDK root.
3431
///
3532
/// If provided, it will be passed to the swift interpreter.
36-
public var sdkRoot: AbsolutePath?
33+
public var sdkRootPath: AbsolutePath?
3734

3835
/// XCTest Location
39-
public var xctestLocation: AbsolutePath?
36+
public var xctestPath: AbsolutePath?
4037

4138
/// Creates the set of manifest resources associated with a `swiftc` executable.
4239
///
4340
/// - Parameters:
44-
/// - swiftCompiler: The absolute path of the associated `swiftc` executable.
45-
/// - swiftCompilerFlags: Extra flags to pass the Swift compiler.: Extra flags to pass the Swift compiler.
46-
/// - libDir: The path of the library resources.
47-
/// - binDir: The bin directory.
48-
/// - sdkRoot: The path to SDK root.
49-
/// - xctestLocation: XCTest Location
41+
/// - swiftCompilerPath: The absolute path of the associated swift compiler executable (`swiftc`).
42+
/// - swiftCompilerFlags: Extra flags to pass to the Swift compiler.
43+
/// - swiftCompilerEnvironment: Environment variables to pass to the Swift compiler.
44+
/// - swiftPMLibrariesRootPath: Custom path for SwiftPM libraries. Computed based on the compiler path by default.
45+
/// - sdkRootPath: Optional path to SDK root.
46+
/// - xctestPath: Optional path to XCTest.
47+
public init(
48+
swiftCompilerPath: AbsolutePath,
49+
swiftCompilerFlags: [String] = [],
50+
swiftCompilerEnvironment: [String: String] = ProcessEnv.vars,
51+
swiftPMLibrariesLocation: SwiftPMLibrariesLocation? = nil,
52+
sdkRootPath: AbsolutePath? = nil,
53+
xctestPath: AbsolutePath? = nil
54+
) {
55+
let swiftPMLibrariesLocation = swiftPMLibrariesLocation ?? {
56+
return .init(swiftCompilerPath: swiftCompilerPath)
57+
}()
58+
59+
self.swiftCompilerPath = swiftCompilerPath
60+
self.swiftCompilerFlags = swiftCompilerFlags
61+
self.swiftCompilerEnvironment = swiftCompilerEnvironment
62+
self.swiftPMLibrariesLocation = swiftPMLibrariesLocation
63+
self.sdkRootPath = sdkRootPath
64+
self.xctestPath = xctestPath
65+
}
66+
67+
// deprecated 8/2021
68+
@available(*, deprecated, message: "use non-deprecated initializer instead")
5069
public init(
5170
swiftCompiler: AbsolutePath,
5271
swiftCompilerFlags: [String] = [],
@@ -56,16 +75,37 @@ public struct ToolchainConfiguration {
5675
sdkRoot: AbsolutePath? = nil,
5776
xctestLocation: AbsolutePath? = nil
5877
) {
59-
self.swiftCompiler = swiftCompiler
60-
self.swiftCompilerFlags = swiftCompilerFlags
61-
self.swiftCompilerEnvironment = swiftCompilerEnvironment
62-
self.libDir = libDir ?? Self.libDir(forBinDir: swiftCompiler.parentDirectory)
63-
self.binDir = binDir
64-
self.sdkRoot = sdkRoot
65-
self.xctestLocation = xctestLocation
78+
self.init(
79+
swiftCompilerPath: swiftCompiler,
80+
swiftCompilerFlags: swiftCompilerFlags,
81+
swiftCompilerEnvironment: swiftCompilerEnvironment,
82+
swiftPMLibrariesLocation: libDir.map { .init(root: $0) },
83+
sdkRootPath: sdkRoot,
84+
xctestPath: xctestLocation
85+
)
6686
}
87+
}
88+
89+
extension ToolchainConfiguration {
90+
public struct SwiftPMLibrariesLocation {
91+
public var manifestAPI: AbsolutePath
92+
public var pluginAPI: AbsolutePath
93+
94+
public init(manifestAPI: AbsolutePath, pluginAPI: AbsolutePath) {
95+
self.manifestAPI = manifestAPI
96+
self.pluginAPI = pluginAPI
97+
}
98+
99+
public init(root: AbsolutePath) {
100+
self.init(
101+
manifestAPI: root.appending(component: "ManifestAPI"),
102+
pluginAPI: root.appending(component: "PluginAPI")
103+
)
104+
}
67105

68-
public static func libDir(forBinDir binDir: AbsolutePath) -> AbsolutePath {
69-
return binDir.parentDirectory.appending(components: "lib", "swift", "pm")
106+
public init(swiftCompilerPath: AbsolutePath) {
107+
let rootPath = swiftCompilerPath.parentDirectory.parentDirectory.appending(components: "lib", "swift", "pm")
108+
self.init(root: rootPath)
109+
}
70110
}
71111
}

Sources/SPMPackageEditor/PackageEditor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ public final class PackageEditorContext {
268268

269269
// Create toolchain.
270270
let hostToolchain = try UserToolchain(destination: .hostDestination())
271-
self.manifestLoader = ManifestLoader(manifestResources: hostToolchain.manifestResources)
271+
self.manifestLoader = ManifestLoader(manifestResources: hostToolchain.configuration)
272272

273273
let repositoriesPath = buildDir.appending(component: "repositories")
274274
self.repositoryManager = RepositoryManager(

Sources/SPMTestSupport/Toolchain.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ extension ToolchainConfiguration {
3535
get {
3636
let toolchain = UserToolchain.default
3737
return .init(
38-
swiftCompiler: toolchain.configuration.swiftCompiler,
38+
swiftCompilerPath: toolchain.configuration.swiftCompilerPath,
3939
swiftCompilerFlags: [],
40-
libDir: toolchain.configuration.libDir,
41-
binDir: toolchain.configuration.binDir
40+
swiftPMLibrariesLocation: toolchain.configuration.swiftPMLibrariesLocation
4241
)
4342
}
4443
}

Sources/Workspace/DefaultPluginScriptRunner.swift

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,33 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner {
4040

4141
public var hostTriple: Triple {
4242
return Self._hostTriple.memoize {
43-
Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompiler)
43+
Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompilerPath)
4444
}
4545
}
4646

4747
/// Helper function that compiles a plugin script as an executable and returns the path to it.
4848
fileprivate func compile(sources: Sources, toolsVersion: ToolsVersion, cacheDir: AbsolutePath) throws -> AbsolutePath {
4949
// FIXME: Much of this is copied from the ManifestLoader and should be consolidated.
5050

51-
// Bin dir will be set when developing swiftpm without building all of the runtimes.
52-
let runtimePath = self.toolchain.binDir ?? self.toolchain.libDir.appending(component: "PluginAPI")
51+
let runtimePath = self.toolchain.swiftPMLibrariesLocation.pluginAPI
5352

5453
// Compile the package plugin script.
55-
var command = [self.toolchain.swiftCompiler.pathString]
54+
var command = [self.toolchain.swiftCompilerPath.pathString]
5655

5756
// FIXME: Workaround for the module cache bug that's been haunting Swift CI
5857
// <rdar://problem/48443680>
5958
let moduleCachePath = ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]
6059

61-
// If we got the binDir that means we could be developing SwiftPM in Xcode
62-
// which produces a framework for dynamic package products.
63-
let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks")
64-
6560
let macOSPackageDescriptionPath: AbsolutePath
66-
if self.toolchain.binDir != nil, localFileSystem.exists(packageFrameworkPath) {
61+
// if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode
62+
// which produces a framework for dynamic package products.
63+
if runtimePath.extension == "framework" {
6764
command += [
68-
"-F", packageFrameworkPath.pathString,
65+
"-F", runtimePath.parentDirectory.pathString,
6966
"-framework", "PackagePlugin",
70-
"-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString,
67+
"-Xlinker", "-rpath", "-Xlinker", runtimePath.parentDirectory.pathString,
7168
]
72-
macOSPackageDescriptionPath = packageFrameworkPath.appending(RelativePath("PackagePlugin.framework/PackagePlugin"))
69+
macOSPackageDescriptionPath = runtimePath.appending(component: "PackagePlugin")
7370
} else {
7471
command += [
7572
"-L", runtimePath.pathString,
@@ -82,7 +79,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner {
8279
#endif
8380

8481
// note: this is not correct for all platforms, but we only actually use it on macOS.
85-
macOSPackageDescriptionPath = runtimePath.appending(RelativePath("libPackagePlugin.dylib"))
82+
macOSPackageDescriptionPath = runtimePath.appending(component: "libPackagePlugin.dylib")
8683
}
8784

8885
// Use the same minimum deployment target as the PackageDescription library (with a fallback of 10.15).
@@ -99,9 +96,15 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner {
9996
command += self.toolchain.swiftCompilerFlags
10097

10198
command += ["-swift-version", toolsVersion.swiftLanguageVersion.rawValue]
102-
command += ["-I", runtimePath.pathString]
99+
// if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode
100+
// which produces a framework for dynamic package products.
101+
if runtimePath.extension == "framework" {
102+
command += ["-I", runtimePath.parentDirectory.parentDirectory.pathString]
103+
} else {
104+
command += ["-I", runtimePath.pathString]
105+
}
103106
#if os(macOS)
104-
if let sdkRoot = self.toolchain.sdkRoot ?? self.sdkRoot() {
107+
if let sdkRoot = self.toolchain.sdkRootPath ?? self.sdkRoot() {
105108
command += ["-sdk", sdkRoot.pathString]
106109
}
107110
#endif

Sources/Workspace/Destination.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,10 @@ public struct Destination: Encodable, Equatable {
9090
environment: [String:String] = ProcessEnv.vars
9191
) throws -> Destination {
9292
// Select the correct binDir.
93-
let customBinDir = ProcessEnv
94-
.vars["SWIFTPM_CUSTOM_BINDIR"]
93+
if ProcessEnv.vars["SWIFTPM_CUSTOM_BINDIR"] != nil {
94+
print("SWIFTPM_CUSTOM_BINDIR was deprecated in favor of SWIFTPM_CUSTOM_BIN_DIR")
95+
}
96+
let customBinDir = (ProcessEnv.vars["SWIFTPM_CUSTOM_BIN_DIR"] ?? ProcessEnv.vars["SWIFTPM_CUSTOM_BINDIR"])
9597
.flatMap{ try? AbsolutePath(validating: $0) }
9698
let binDir = try customBinDir ?? binDir ?? Destination.hostBinDir(
9799
originalWorkingDirectory: originalWorkingDirectory)

0 commit comments

Comments
 (0)