diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0befd5025b..c4f64596ac6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -344,7 +344,7 @@ $> SWIFT_EXEC=/path/to/my/built/swiftc swift build SwiftPM computes the path of its runtime libraries relative to where it is installed. This path can be overridden by setting the environment variable -`SWIFTPM_PD_LIBS` to a directory containing the libraries, or a colon-separated list of +`SWIFTPM_CUSTOM_LIBS_DIR` to a directory containing the libraries, or a colon-separated list of absolute search paths. SwiftPM will choose the first path which exists on disk. If none of the paths are present on disk, it will fall back to built-in computation. diff --git a/Sources/Commands/SwiftRunTool.swift b/Sources/Commands/SwiftRunTool.swift index 201d86b6408..cdc7adb4206 100644 --- a/Sources/Commands/SwiftRunTool.swift +++ b/Sources/Commands/SwiftRunTool.swift @@ -136,7 +136,7 @@ public struct SwiftRunTool: SwiftCommand { let arguments = buildOp.buildPlan!.createREPLArguments() print("Launching Swift REPL with arguments: \(arguments.joined(separator: " "))") try run( - swiftTool.getToolchain().swiftInterpreter, + swiftTool.getToolchain().swiftInterpreterPath, originalWorkingDirectory: swiftTool.originalWorkingDirectory, arguments: arguments) @@ -171,7 +171,7 @@ public struct SwiftRunTool: SwiftCommand { if let executable = options.executable, isValidSwiftFilePath(executable) { swiftTool.diagnostics.emit(.runFileDeprecation) // Redirect execution to the toolchain's swift executable. - let swiftInterpreterPath = try swiftTool.getToolchain().swiftInterpreter + let swiftInterpreterPath = try swiftTool.getToolchain().swiftInterpreterPath // Prepend the script to interpret to the arguments. let arguments = [executable] + options.arguments try run( diff --git a/Sources/Commands/SwiftTestTool.swift b/Sources/Commands/SwiftTestTool.swift index 1f7815a3f91..738bbee439e 100644 --- a/Sources/Commands/SwiftTestTool.swift +++ b/Sources/Commands/SwiftTestTool.swift @@ -1039,10 +1039,9 @@ fileprivate func constructTestEnvironment( let codecovProfile = buildParameters.buildPath.appending(components: "codecov", "default%m.profraw") env["LLVM_PROFILE_FILE"] = codecovProfile.pathString } - #if !os(macOS) #if os(Windows) - if let location = toolchain.manifestResources.xctestLocation { + if let location = toolchain.configuration.xctestPath { env["Path"] = "\(location.pathString);\(env["Path"] ?? "")" } #endif diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index ee219676c34..4407349dfc2 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -689,21 +689,20 @@ public final class ManifestLoader: ManifestLoaderProtocol { let moduleCachePath = (ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]).flatMap{ AbsolutePath.init($0) } var cmd: [String] = [] - cmd += [self.toolchain.swiftCompiler.pathString] + cmd += [self.toolchain.swiftCompilerPath.pathString] cmd += verbosity.ccArgs let macOSPackageDescriptionPath: AbsolutePath - // If we got the binDir that means we could be developing SwiftPM in Xcode + // if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode // which produces a framework for dynamic package products. - let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks") - if self.toolchain.binDir != nil, localFileSystem.exists(packageFrameworkPath) { + if runtimePath.extension == "framework" { cmd += [ - "-F", packageFrameworkPath.pathString, + "-F", runtimePath.parentDirectory.pathString, "-framework", "PackageDescription", - "-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString, + "-Xlinker", "-rpath", "-Xlinker", runtimePath.parentDirectory.pathString, ] - macOSPackageDescriptionPath = packageFrameworkPath.appending(RelativePath("PackageDescription.framework/PackageDescription")) + macOSPackageDescriptionPath = runtimePath.appending(component: "PackageDescription") } else { cmd += [ "-L", runtimePath.pathString, @@ -716,13 +715,13 @@ public final class ManifestLoader: ManifestLoaderProtocol { #endif // note: this is not correct for all platforms, but we only actually use it on macOS. - macOSPackageDescriptionPath = runtimePath.appending(RelativePath("libPackageDescription.dylib")) + macOSPackageDescriptionPath = runtimePath.appending(component: "libPackageDescription.dylib") } // Use the same minimum deployment target as the PackageDescription library (with a fallback of 10.15). #if os(macOS) let triple = Self._hostTriple.memoize { - Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompiler) + Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompilerPath) } let version = try Self._packageDescriptionMinimumDeploymentTarget.memoize { @@ -853,9 +852,15 @@ public final class ManifestLoader: ManifestLoaderProtocol { var cmd = [String]() let runtimePath = self.runtimePath(for: toolsVersion) cmd += ["-swift-version", toolsVersion.swiftLanguageVersion.rawValue] - cmd += ["-I", runtimePath.pathString] + // if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode + // which produces a framework for dynamic package products. + if runtimePath.extension == "framework" { + cmd += ["-I", runtimePath.parentDirectory.parentDirectory.pathString] + } else { + cmd += ["-I", runtimePath.pathString] + } #if os(macOS) - if let sdkRoot = self.toolchain.sdkRoot ?? self.sdkRoot() { + if let sdkRoot = self.toolchain.sdkRootPath ?? self.sdkRoot() { cmd += ["-sdk", sdkRoot.pathString] } #endif @@ -865,19 +870,14 @@ public final class ManifestLoader: ManifestLoaderProtocol { /// Returns the runtime path given the manifest version and path to libDir. private func runtimePath(for version: ToolsVersion) -> AbsolutePath { - // Bin dir will be set when developing swiftpm without building all of the runtimes. - if let binDir = self.toolchain.binDir { - return binDir - } - - // Otherwise we use the standard location of the manifest API in the toolchain, if it exists. - let manifestAPIDir = self.toolchain.libDir.appending(component: "ManifestAPI") + let manifestAPIDir = self.toolchain.swiftPMLibrariesLocation.manifestAPI if localFileSystem.exists(manifestAPIDir) { return manifestAPIDir } - - // Otherwise, fall back on the old location (this would indicate that we're using an old toolchain). - return self.toolchain.libDir.appending(version.runtimeSubpath) + + // FIXME: how do we test this? + // Fall back on the old location (this would indicate that we're using an old toolchain). + return self.toolchain.swiftPMLibrariesLocation.manifestAPI.parentDirectory.appending(version.runtimeSubpath) } /// Returns path to the manifest database inside the given cache directory. diff --git a/Sources/PackageModel/ToolchainConfiguration.swift b/Sources/PackageModel/ToolchainConfiguration.swift index d77053df3ec..ad7bd83a5be 100644 --- a/Sources/PackageModel/ToolchainConfiguration.swift +++ b/Sources/PackageModel/ToolchainConfiguration.swift @@ -16,7 +16,7 @@ import TSCBasic /// using the package manager with alternate toolchains in the future. public struct ToolchainConfiguration { /// The path of the swift compiler. - public var swiftCompiler: AbsolutePath + public var swiftCompilerPath: AbsolutePath /// Extra arguments to pass the Swift compiler (defaults to the empty string). public var swiftCompilerFlags: [String] @@ -24,29 +24,48 @@ public struct ToolchainConfiguration { /// Environment to pass to the Swift compiler (defaults to the inherited environment). public var swiftCompilerEnvironment: [String: String] - /// The path of the library resources. - public var libDir: AbsolutePath - - /// The bin directory. - public var binDir: AbsolutePath? + /// SwiftPM library paths. + public var swiftPMLibrariesLocation: SwiftPMLibrariesLocation /// The path to SDK root. /// /// If provided, it will be passed to the swift interpreter. - public var sdkRoot: AbsolutePath? + public var sdkRootPath: AbsolutePath? /// XCTest Location - public var xctestLocation: AbsolutePath? + public var xctestPath: AbsolutePath? /// Creates the set of manifest resources associated with a `swiftc` executable. /// /// - Parameters: - /// - swiftCompiler: The absolute path of the associated `swiftc` executable. - /// - swiftCompilerFlags: Extra flags to pass the Swift compiler.: Extra flags to pass the Swift compiler. - /// - libDir: The path of the library resources. - /// - binDir: The bin directory. - /// - sdkRoot: The path to SDK root. - /// - xctestLocation: XCTest Location + /// - swiftCompilerPath: The absolute path of the associated swift compiler executable (`swiftc`). + /// - swiftCompilerFlags: Extra flags to pass to the Swift compiler. + /// - swiftCompilerEnvironment: Environment variables to pass to the Swift compiler. + /// - swiftPMLibrariesRootPath: Custom path for SwiftPM libraries. Computed based on the compiler path by default. + /// - sdkRootPath: Optional path to SDK root. + /// - xctestPath: Optional path to XCTest. + public init( + swiftCompilerPath: AbsolutePath, + swiftCompilerFlags: [String] = [], + swiftCompilerEnvironment: [String: String] = ProcessEnv.vars, + swiftPMLibrariesLocation: SwiftPMLibrariesLocation? = nil, + sdkRootPath: AbsolutePath? = nil, + xctestPath: AbsolutePath? = nil + ) { + let swiftPMLibrariesLocation = swiftPMLibrariesLocation ?? { + return .init(swiftCompilerPath: swiftCompilerPath) + }() + + self.swiftCompilerPath = swiftCompilerPath + self.swiftCompilerFlags = swiftCompilerFlags + self.swiftCompilerEnvironment = swiftCompilerEnvironment + self.swiftPMLibrariesLocation = swiftPMLibrariesLocation + self.sdkRootPath = sdkRootPath + self.xctestPath = xctestPath + } + + // deprecated 8/2021 + @available(*, deprecated, message: "use non-deprecated initializer instead") public init( swiftCompiler: AbsolutePath, swiftCompilerFlags: [String] = [], @@ -56,16 +75,37 @@ public struct ToolchainConfiguration { sdkRoot: AbsolutePath? = nil, xctestLocation: AbsolutePath? = nil ) { - self.swiftCompiler = swiftCompiler - self.swiftCompilerFlags = swiftCompilerFlags - self.swiftCompilerEnvironment = swiftCompilerEnvironment - self.libDir = libDir ?? Self.libDir(forBinDir: swiftCompiler.parentDirectory) - self.binDir = binDir - self.sdkRoot = sdkRoot - self.xctestLocation = xctestLocation + self.init( + swiftCompilerPath: swiftCompiler, + swiftCompilerFlags: swiftCompilerFlags, + swiftCompilerEnvironment: swiftCompilerEnvironment, + swiftPMLibrariesLocation: libDir.map { .init(root: $0) }, + sdkRootPath: sdkRoot, + xctestPath: xctestLocation + ) } +} + +extension ToolchainConfiguration { + public struct SwiftPMLibrariesLocation { + public var manifestAPI: AbsolutePath + public var pluginAPI: AbsolutePath + + public init(manifestAPI: AbsolutePath, pluginAPI: AbsolutePath) { + self.manifestAPI = manifestAPI + self.pluginAPI = pluginAPI + } + + public init(root: AbsolutePath) { + self.init( + manifestAPI: root.appending(component: "ManifestAPI"), + pluginAPI: root.appending(component: "PluginAPI") + ) + } - public static func libDir(forBinDir binDir: AbsolutePath) -> AbsolutePath { - return binDir.parentDirectory.appending(components: "lib", "swift", "pm") + public init(swiftCompilerPath: AbsolutePath) { + let rootPath = swiftCompilerPath.parentDirectory.parentDirectory.appending(components: "lib", "swift", "pm") + self.init(root: rootPath) + } } } diff --git a/Sources/SPMPackageEditor/PackageEditor.swift b/Sources/SPMPackageEditor/PackageEditor.swift index 3e99b6b2362..66f88b89ca7 100644 --- a/Sources/SPMPackageEditor/PackageEditor.swift +++ b/Sources/SPMPackageEditor/PackageEditor.swift @@ -268,7 +268,7 @@ public final class PackageEditorContext { // Create toolchain. let hostToolchain = try UserToolchain(destination: .hostDestination()) - self.manifestLoader = ManifestLoader(manifestResources: hostToolchain.manifestResources) + self.manifestLoader = ManifestLoader(manifestResources: hostToolchain.configuration) let repositoriesPath = buildDir.appending(component: "repositories") self.repositoryManager = RepositoryManager( diff --git a/Sources/SPMTestSupport/Toolchain.swift b/Sources/SPMTestSupport/Toolchain.swift index 31cd15d35fb..bbb54316d78 100644 --- a/Sources/SPMTestSupport/Toolchain.swift +++ b/Sources/SPMTestSupport/Toolchain.swift @@ -35,10 +35,9 @@ extension ToolchainConfiguration { get { let toolchain = UserToolchain.default return .init( - swiftCompiler: toolchain.configuration.swiftCompiler, + swiftCompilerPath: toolchain.configuration.swiftCompilerPath, swiftCompilerFlags: [], - libDir: toolchain.configuration.libDir, - binDir: toolchain.configuration.binDir + swiftPMLibrariesLocation: toolchain.configuration.swiftPMLibrariesLocation ) } } diff --git a/Sources/Workspace/DefaultPluginScriptRunner.swift b/Sources/Workspace/DefaultPluginScriptRunner.swift index 0719480e34f..869f10b83b5 100644 --- a/Sources/Workspace/DefaultPluginScriptRunner.swift +++ b/Sources/Workspace/DefaultPluginScriptRunner.swift @@ -40,7 +40,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner { public var hostTriple: Triple { return Self._hostTriple.memoize { - Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompiler) + Triple.getHostTriple(usingSwiftCompiler: self.toolchain.swiftCompilerPath) } } @@ -48,28 +48,25 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner { fileprivate func compile(sources: Sources, toolsVersion: ToolsVersion, cacheDir: AbsolutePath) throws -> AbsolutePath { // FIXME: Much of this is copied from the ManifestLoader and should be consolidated. - // Bin dir will be set when developing swiftpm without building all of the runtimes. - let runtimePath = self.toolchain.binDir ?? self.toolchain.libDir.appending(component: "PluginAPI") + let runtimePath = self.toolchain.swiftPMLibrariesLocation.pluginAPI // Compile the package plugin script. - var command = [self.toolchain.swiftCompiler.pathString] + var command = [self.toolchain.swiftCompilerPath.pathString] // FIXME: Workaround for the module cache bug that's been haunting Swift CI // let moduleCachePath = ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] - // If we got the binDir that means we could be developing SwiftPM in Xcode - // which produces a framework for dynamic package products. - let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks") - let macOSPackageDescriptionPath: AbsolutePath - if self.toolchain.binDir != nil, localFileSystem.exists(packageFrameworkPath) { + // if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode + // which produces a framework for dynamic package products. + if runtimePath.extension == "framework" { command += [ - "-F", packageFrameworkPath.pathString, + "-F", runtimePath.parentDirectory.pathString, "-framework", "PackagePlugin", - "-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString, + "-Xlinker", "-rpath", "-Xlinker", runtimePath.parentDirectory.pathString, ] - macOSPackageDescriptionPath = packageFrameworkPath.appending(RelativePath("PackagePlugin.framework/PackagePlugin")) + macOSPackageDescriptionPath = runtimePath.appending(component: "PackagePlugin") } else { command += [ "-L", runtimePath.pathString, @@ -82,7 +79,7 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner { #endif // note: this is not correct for all platforms, but we only actually use it on macOS. - macOSPackageDescriptionPath = runtimePath.appending(RelativePath("libPackagePlugin.dylib")) + macOSPackageDescriptionPath = runtimePath.appending(component: "libPackagePlugin.dylib") } // Use the same minimum deployment target as the PackageDescription library (with a fallback of 10.15). @@ -99,9 +96,15 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner { command += self.toolchain.swiftCompilerFlags command += ["-swift-version", toolsVersion.swiftLanguageVersion.rawValue] - command += ["-I", runtimePath.pathString] + // if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode + // which produces a framework for dynamic package products. + if runtimePath.extension == "framework" { + command += ["-I", runtimePath.parentDirectory.parentDirectory.pathString] + } else { + command += ["-I", runtimePath.pathString] + } #if os(macOS) - if let sdkRoot = self.toolchain.sdkRoot ?? self.sdkRoot() { + if let sdkRoot = self.toolchain.sdkRootPath ?? self.sdkRoot() { command += ["-sdk", sdkRoot.pathString] } #endif diff --git a/Sources/Workspace/Destination.swift b/Sources/Workspace/Destination.swift index edf1db57cd1..6fdb8c2688e 100644 --- a/Sources/Workspace/Destination.swift +++ b/Sources/Workspace/Destination.swift @@ -90,8 +90,10 @@ public struct Destination: Encodable, Equatable { environment: [String:String] = ProcessEnv.vars ) throws -> Destination { // Select the correct binDir. - let customBinDir = ProcessEnv - .vars["SWIFTPM_CUSTOM_BINDIR"] + if ProcessEnv.vars["SWIFTPM_CUSTOM_BINDIR"] != nil { + print("SWIFTPM_CUSTOM_BINDIR was deprecated in favor of SWIFTPM_CUSTOM_BIN_DIR") + } + let customBinDir = (ProcessEnv.vars["SWIFTPM_CUSTOM_BIN_DIR"] ?? ProcessEnv.vars["SWIFTPM_CUSTOM_BINDIR"]) .flatMap{ try? AbsolutePath(validating: $0) } let binDir = try customBinDir ?? binDir ?? Destination.hostBinDir( originalWorkingDirectory: originalWorkingDirectory) diff --git a/Sources/Workspace/UserToolchain.swift b/Sources/Workspace/UserToolchain.swift index 8787f88a7af..7fd02200c42 100644 --- a/Sources/Workspace/UserToolchain.swift +++ b/Sources/Workspace/UserToolchain.swift @@ -24,12 +24,21 @@ private let hostExecutableSuffix = "" // FIXME: This is messy and needs a redesign. public final class UserToolchain: Toolchain { + public typealias SwiftCompilers = (compile: AbsolutePath, manifest: AbsolutePath) /// The manifest resource provider. public let configuration: ToolchainConfiguration /// Path of the `swiftc` compiler. - public let swiftCompiler: AbsolutePath + public let swiftCompilerPath: AbsolutePath + + // deprecated 8/2021 + @available(*, deprecated, message: "use swiftCompilerPath instead") + public var swiftCompiler: AbsolutePath { + get { + self.swiftCompilerPath + } + } public var extraCCFlags: [String] @@ -44,8 +53,14 @@ public final class UserToolchain: Toolchain { } /// Path of the `swift` interpreter. + public var swiftInterpreterPath: AbsolutePath { + return self.swiftCompilerPath.parentDirectory.appending(component: "swift" + hostExecutableSuffix) + } + + // deprecated 8/2021 + @available(*, deprecated, message: "use swiftInterpreterPath instead") public var swiftInterpreter: AbsolutePath { - return swiftCompiler.parentDirectory.appending(component: "swift" + hostExecutableSuffix) + return self.swiftInterpreterPath } /// Path to the xctest utility. @@ -65,13 +80,15 @@ public final class UserToolchain: Toolchain { /// Search paths from the PATH environment variable. let envSearchPaths: [AbsolutePath] + private var _clangCompiler: AbsolutePath? + /// Returns the runtime library for the given sanitizer. public func runtimeLibrary(for sanitizer: Sanitizer) throws -> AbsolutePath { // FIXME: This is only for SwiftPM development time support. It is OK // for now but we shouldn't need to resolve the symlink. We need to lay // down symlinks to runtimes in our fake toolchain as part of the // bootstrap script. - let swiftCompiler = resolveSymlinks(self.swiftCompiler) + let swiftCompiler = resolveSymlinks(self.swiftCompilerPath) let runtime = swiftCompiler.appending( RelativePath("../../lib/swift/clang/lib/darwin/libclang_rt.\(sanitizer.shortName)_osx_dynamic.dylib")) @@ -84,6 +101,12 @@ public final class UserToolchain: Toolchain { return runtime } + // MARK: - private utilities + + private static func lookup(variable: String, searchPaths: [AbsolutePath]) -> AbsolutePath? { + return lookupExecutablePath(filename: ProcessEnv.vars[variable], searchPaths: searchPaths) + } + private static func getTool(_ name: String, binDir: AbsolutePath) throws -> AbsolutePath { let executableName = "\(name)\(hostExecutableSuffix)" let toolPath = binDir.appending(component: executableName) @@ -107,7 +130,7 @@ public final class UserToolchain: Toolchain { #endif } - public typealias SwiftCompilers = (compile: AbsolutePath, manifest: AbsolutePath) + // MARK: - public API /// Determines the Swift compiler paths for compilation and manifest parsing. public static func determineSwiftCompilers(binDir: AbsolutePath) throws -> SwiftCompilers { @@ -151,83 +174,78 @@ public final class UserToolchain: Toolchain { return (SWIFT_EXEC ?? resolvedBinDirCompiler, SWIFT_EXEC_MANIFEST ?? resolvedBinDirCompiler) } - private static func lookup(variable: String, searchPaths: [AbsolutePath]) -> AbsolutePath? { - return lookupExecutablePath(filename: ProcessEnv.vars[variable], searchPaths: searchPaths) - } - /// Returns the path to clang compiler tool. public func getClangCompiler() throws -> AbsolutePath { // Check if we already computed. - if let clang = _clangCompiler { + if let clang = self._clangCompiler { return clang } // Check in the environment variable first. - if let toolPath = UserToolchain.lookup(variable: "CC", searchPaths: envSearchPaths) { - _clangCompiler = toolPath + if let toolPath = UserToolchain.lookup(variable: "CC", searchPaths: self.envSearchPaths) { + self._clangCompiler = toolPath return toolPath } // Then, check the toolchain. do { - if let toolPath = try? UserToolchain.getTool("clang", binDir: destination.binDir) { - _clangCompiler = toolPath + if let toolPath = try? UserToolchain.getTool("clang", binDir: self.destination.binDir) { + self._clangCompiler = toolPath return toolPath } } // Otherwise, lookup it up on the system. - let toolPath = try UserToolchain.findTool("clang", envSearchPaths: envSearchPaths) - _clangCompiler = toolPath + let toolPath = try UserToolchain.findTool("clang", envSearchPaths: self.envSearchPaths) + self._clangCompiler = toolPath return toolPath } - private var _clangCompiler: AbsolutePath? public func _isClangCompilerVendorApple() throws -> Bool? { // Assume the vendor is Apple on macOS. // FIXME: This might not be the best way to determine this. - #if os(macOS) +#if os(macOS) return true - #else +#else return false - #endif +#endif } /// Returns the path to lldb. public func getLLDB() throws -> AbsolutePath { // Look for LLDB next to the compiler first. - if let lldbPath = try? UserToolchain.getTool("lldb", binDir: swiftCompiler.parentDirectory) { + if let lldbPath = try? UserToolchain.getTool("lldb", binDir: self.swiftCompilerPath.parentDirectory) { return lldbPath } // If that fails, fall back to xcrun, PATH, etc. - return try UserToolchain.findTool("lldb", envSearchPaths: envSearchPaths) + return try UserToolchain.findTool("lldb", envSearchPaths: self.envSearchPaths) } /// Returns the path to llvm-cov tool. public func getLLVMCov() throws -> AbsolutePath { - return try UserToolchain.getTool("llvm-cov", binDir: destination.binDir) + return try UserToolchain.getTool("llvm-cov", binDir: self.destination.binDir) } /// Returns the path to llvm-prof tool. public func getLLVMProf() throws -> AbsolutePath { - return try UserToolchain.getTool("llvm-profdata", binDir: destination.binDir) + return try UserToolchain.getTool("llvm-profdata", binDir: self.destination.binDir) } public func getSwiftAPIDigester() throws -> AbsolutePath { - if let envValue = UserToolchain.lookup(variable: "SWIFT_API_DIGESTER", searchPaths: envSearchPaths) { + if let envValue = UserToolchain.lookup(variable: "SWIFT_API_DIGESTER", searchPaths: self.envSearchPaths) { return envValue } - return try UserToolchain.getTool("swift-api-digester", binDir: swiftCompiler.parentDirectory) + return try UserToolchain.getTool("swift-api-digester", binDir: self.swiftCompilerPath.parentDirectory) } public func getSymbolGraphExtract() throws -> AbsolutePath { - if let envValue = UserToolchain.lookup(variable: "SWIFT_SYMBOLGRAPH_EXTRACT", searchPaths: envSearchPaths) { + if let envValue = UserToolchain.lookup(variable: "SWIFT_SYMBOLGRAPH_EXTRACT", searchPaths: self.envSearchPaths) { return envValue } - return try UserToolchain.getTool("swift-symbolgraph-extract", binDir: swiftCompiler.parentDirectory) + return try UserToolchain.getTool("swift-symbolgraph-extract", binDir: self.swiftCompilerPath.parentDirectory) } - public static func deriveSwiftCFlags(triple: Triple, destination: Destination) -> [String] { + internal static func deriveSwiftCFlags(triple: Triple, destination: Destination) -> [String] { guard let sdk = destination.sdk else { if triple.isWindows() { // Windows uses a variable named SDKROOT to determine the root of @@ -253,15 +271,15 @@ public final class UserToolchain: Toolchain { } if let DEVELOPER_DIR = ProcessEnv.vars["DEVELOPER_DIR"], - let root = try? AbsolutePath(validating: DEVELOPER_DIR) - .appending(component: "Platforms") - .appending(component: "Windows.platform") { + let root = try? AbsolutePath(validating: DEVELOPER_DIR) + .appending(component: "Platforms") + .appending(component: "Windows.platform") { if let info = WindowsPlatformInfo(reading: root.appending(component: "Info.plist"), diagnostics: nil, filesystem: localFileSystem) { let path: AbsolutePath = - root.appending(component: "Developer") - .appending(component: "Library") - .appending(component: "XCTest-\(info.defaults.xctestVersion)") + root.appending(component: "Developer") + .appending(component: "Library") + .appending(component: "XCTest-\(info.defaults.xctestVersion)") xctest = [ "-I", path.appending(RelativePath("usr/lib/swift/windows/\(triple.arch)")).pathString, "-L", path.appending(RelativePath("usr/lib/swift/windows")).pathString, @@ -279,11 +297,13 @@ public final class UserToolchain: Toolchain { } return (triple.isDarwin() || triple.isAndroid() || triple.isWASI() - ? ["-sdk", sdk.pathString] - : []) - + destination.extraSwiftCFlags + ? ["-sdk", sdk.pathString] + : []) + + destination.extraSwiftCFlags } + // MARK: - initializer + public init(destination: Destination, environment: [String: String] = ProcessEnv.vars) throws { self.destination = destination @@ -297,7 +317,7 @@ public final class UserToolchain: Toolchain { let binDir = destination.binDir let swiftCompilers = try UserToolchain.determineSwiftCompilers(binDir: binDir) - self.swiftCompiler = swiftCompilers.compile + self.swiftCompilerPath = swiftCompilers.compile self.archs = destination.archs // Use the triple from destination or compute the host triple using swiftc. @@ -318,12 +338,12 @@ public final class UserToolchain: Toolchain { let xctestFindArgs = ["/usr/bin/xcrun", "--sdk", "macosx", "--find", "xctest"] self.xctest = try AbsolutePath( validating: Process.checkNonZeroExit(arguments: xctestFindArgs, environment: environment - ).spm_chomp()) + ).spm_chomp()) } else { self.xctest = nil } - self.extraSwiftCFlags = UserToolchain.deriveSwiftCFlags(triple: triple, destination: destination) + self.extraSwiftCFlags = Self.deriveSwiftCFlags(triple: triple, destination: destination) if let sdk = destination.sdk { self.extraCCFlags = [ @@ -365,11 +385,28 @@ public final class UserToolchain: Toolchain { } } - // Compute the path of directory containing the PackageDescription libraries. - var pdLibDir = ToolchainConfiguration.libDir(forBinDir: binDir) + let swiftPMLibrariesLocation = try Self.deriveSwiftPMLibrariesLocation(swiftCompilerPath: swiftCompilerPath, destination: destination) + + let xctestPath = Self.deriveXCTestPath() + + self.configuration = .init( + swiftCompilerPath: swiftCompilers.manifest, + swiftCompilerFlags: self.extraSwiftCFlags, + swiftPMLibrariesLocation: swiftPMLibrariesLocation, + sdkRootPath: self.destination.sdk, + xctestPath: xctestPath + ) + } + private static func deriveSwiftPMLibrariesLocation( + swiftCompilerPath: AbsolutePath, + destination: Destination + ) throws -> ToolchainConfiguration.SwiftPMLibrariesLocation? { // Look for an override in the env. - if let pdLibDirEnvStr = ProcessEnv.vars["SWIFTPM_PD_LIBS"] { + if let pathEnvVariable = ProcessEnv.vars["SWIFTPM_CUSTOM_LIBS_DIR"] ?? ProcessEnv.vars["SWIFTPM_PD_LIBS"] { + if ProcessEnv.vars["SWIFTPM_PD_LIBS"] != nil { + print("SWIFTPM_PD_LIBS was deprecated in favor of SWIFTPM_CUSTOM_LIBS_DIR") + } // We pick the first path which exists in an environment variable // delimited by the platform specific string separator. #if os(Windows) @@ -377,46 +414,70 @@ public final class UserToolchain: Toolchain { #else let separator: Character = ":" #endif - let paths = pdLibDirEnvStr.split(separator: separator).map(String.init) - var foundPDLibDir = false + let paths = pathEnvVariable.split(separator: separator).map(String.init) for pathString in paths { if let path = try? AbsolutePath(validating: pathString), localFileSystem.exists(path) { - pdLibDir = path - foundPDLibDir = true - break + // we found the custom one + return .init(root: path) } } - if !foundPDLibDir { - throw InternalError("Couldn't find any SWIFTPM_PD_LIBS directory: \(pdLibDirEnvStr)") - } + // fail if custom one specified but not found + throw InternalError("Couldn't find the custom libraries location defined by SWIFTPM_CUSTOM_LIBS_DIR / SWIFTPM_PD_LIBS: \(pathEnvVariable)") } - var xctestLocation: AbsolutePath? + // FIXME: the following logic is pretty fragile, but has always been this way + // an alternative cloud be to force explicit locations to always be set explicitly when running in XCode/SwiftPM + // debug and assert if not set but we detect that we are in this mode + + let applicationPath = destination.binDir + + // this is the normal case when using the toolchain + let librariesPath = applicationPath.parentDirectory.appending(components: "lib", "swift", "pm") + if localFileSystem.exists(librariesPath) { + return .init(root: librariesPath) + } + + // this tests if we are debugging / testing SwiftPM with Xcode + let manifestFrameworksPath = applicationPath.appending(components: "PackageFrameworks", "PackageDescription.framework") + let pluginFrameworksPath = applicationPath.appending(components: "PackageFrameworks", "PackagePlugin.framework") + if localFileSystem.exists(manifestFrameworksPath) && localFileSystem.exists(pluginFrameworksPath) { + return .init( + manifestAPI: manifestFrameworksPath, + pluginAPI: pluginFrameworksPath + ) + } + + // this tests if we are debugging / testing SwiftPM with SwiftPM + if localFileSystem.exists(applicationPath.appending(component: "swift-package")) { + return .init( + manifestAPI: applicationPath, + pluginAPI: applicationPath + ) + } + + // we are using a SwiftPM outside a toolchain, use the compiler path to compute the location + return .init(swiftCompilerPath: swiftCompilerPath) + } + + // TODO: why is this only required on Windows? is there something better we can do? + private static func deriveXCTestPath() -> AbsolutePath? { #if os(Windows) if let DEVELOPER_DIR = ProcessEnv.vars["DEVELOPER_DIR"], - let root = try? AbsolutePath(validating: DEVELOPER_DIR) - .appending(component: "Platforms") - .appending(component: "Windows.platform") { + let root = try? AbsolutePath(validating: DEVELOPER_DIR) + .appending(component: "Platforms") + .appending(component: "Windows.platform") { if let info = WindowsPlatformInfo(reading: root.appending(component: "Info.plist"), - diagnostics: nil, filesystem: localFileSystem) { - xctestLocation = root.appending(component: "Developer") - .appending(component: "Library") - .appending(component: "XCTest-\(info.defaults.xctestVersion)") - .appending(component: "usr") - .appending(component: "bin") + diagnostics: nil, + filesystem: localFileSystem) { + return root.appending(component: "Developer") + .appending(component: "Library") + .appending(component: "XCTest-\(info.defaults.xctestVersion)") + .appending(component: "usr") + .appending(component: "bin") } } #endif - - self.configuration = .init( - swiftCompiler: swiftCompilers.manifest, - swiftCompilerFlags: self.extraSwiftCFlags, - libDir: pdLibDir, - // Set the bin directory if we don't have a lib dir. - binDir: localFileSystem.exists(pdLibDir) ? nil : binDir, - sdkRoot: self.destination.sdk, - xctestLocation: xctestLocation - ) + return nil } } diff --git a/Tests/CommandsTests/BuildToolTests.swift b/Tests/CommandsTests/BuildToolTests.swift index 554caf74a5e..70e8beadd5f 100644 --- a/Tests/CommandsTests/BuildToolTests.swift +++ b/Tests/CommandsTests/BuildToolTests.swift @@ -302,12 +302,12 @@ final class BuildToolTests: XCTestCase { fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { path in // Try building using XCBuild without specifying overrides. This should succeed, and should use the default compiler path. let defaultOutput = try execute(["-c", "debug", "-v"], packagePath: path).stdout - XCTAssert(defaultOutput.contains(ToolchainConfiguration.default.swiftCompiler.pathString), defaultOutput) + XCTAssert(defaultOutput.contains(ToolchainConfiguration.default.swiftCompilerPath.pathString), defaultOutput) // Now try building using XCBuild while specifying a faulty compiler override. This should fail. Note that we need to set the executable to use for the manifest itself to the default one, since it defaults to SWIFT_EXEC if not provided. var overriddenOutput = "" do { - overriddenOutput = try execute(["-c", "debug", "-v"], environment: ["SWIFT_EXEC": "/usr/bin/false", "SWIFT_EXEC_MANIFEST": ToolchainConfiguration.default.swiftCompiler.pathString], packagePath: path).stdout + overriddenOutput = try execute(["-c", "debug", "-v"], environment: ["SWIFT_EXEC": "/usr/bin/false", "SWIFT_EXEC_MANIFEST": ToolchainConfiguration.default.swiftCompilerPath.pathString], packagePath: path).stdout XCTFail("unexpected success (was SWIFT_EXEC not overridden properly?)") } catch SwiftPMProductError.executionFailure(let error, let stdout, _) { diff --git a/Tests/FunctionalPerformanceTests/BuildPerfTests.swift b/Tests/FunctionalPerformanceTests/BuildPerfTests.swift index ec116efbe0a..bb8e7a88f1d 100644 --- a/Tests/FunctionalPerformanceTests/BuildPerfTests.swift +++ b/Tests/FunctionalPerformanceTests/BuildPerfTests.swift @@ -21,7 +21,7 @@ class BuildPerfTests: XCTestCasePerf { @discardableResult func execute(args: [String] = [], packagePath: AbsolutePath) throws -> (stdout: String, stderr: String) { // FIXME: We should pass the SWIFT_EXEC at lower level. - return try SwiftPMProduct.SwiftBuild.execute(args + [], packagePath: packagePath, env: ["SWIFT_EXEC": ToolchainConfiguration.default.swiftCompiler.pathString]) + return try SwiftPMProduct.SwiftBuild.execute(args + [], packagePath: packagePath, env: ["SWIFT_EXEC": ToolchainConfiguration.default.swiftCompilerPath.pathString]) } func clean(packagePath: AbsolutePath) throws { diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 713f6bc88ac..e94ea92a959 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -8,19 +8,17 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import XCTest - +import Basics import PackageGraph import PackageLoading import PackageModel import SourceControl import SPMBuildCore +import SPMTestSupport import TSCBasic import TSCUtility @testable import Workspace -import Basics - -import SPMTestSupport +import XCTest final class WorkspaceTests: XCTestCase { func testBasics() throws { diff --git a/Tests/XcodeprojTests/FunctionalTests.swift b/Tests/XcodeprojTests/FunctionalTests.swift index 6c9600fb143..9b29defab80 100644 --- a/Tests/XcodeprojTests/FunctionalTests.swift +++ b/Tests/XcodeprojTests/FunctionalTests.swift @@ -139,7 +139,7 @@ func XCTAssertXcodeBuild(project: AbsolutePath, file: StaticString = #file, line env["TOOLCHAINS"] = "default" } let xcconfig = project.appending(component: "overrides.xcconfig") - let swiftCompilerPath = ToolchainConfiguration.default.swiftCompiler + let swiftCompilerPath = ToolchainConfiguration.default.swiftCompilerPath // Override path to the Swift compiler. let stream = BufferedOutputByteStream() diff --git a/Utilities/bootstrap b/Utilities/bootstrap index d3fce194aa0..43463bc9ed5 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -589,7 +589,7 @@ def build_swiftpm_with_swiftpm(args, integrated_swift_driver): if args.bootstrap: note("Building SwiftPM (with a freshly built swift-build)") - swiftpm_args.append("SWIFTPM_PD_LIBS=" + os.path.join(args.bootstrap_dir, "pm")) + swiftpm_args.append("SWIFTPM_CUSTOM_LIBS_DIR=" + os.path.join(args.bootstrap_dir, "pm")) swiftpm_args.append(os.path.join(args.bootstrap_dir, "bin/swift-build")) else: note("Building SwiftPM (with a prebuilt swift-build)")